From 1c82c6145a00f3abba3fb8de984b8ad837746f33 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:14:13 +0000 Subject: [PATCH 1/3] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) 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. From b9fafaa488beee73aa071562a1efb355cc8dcb9c Mon Sep 17 00:00:00 2001 From: Younes Date: Wed, 10 Dec 2025 18:20:50 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Implenterat=20VG=20nivv=C3=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/Main.java | 200 +++++++++++++++++- .../SimpleDriverManagerDataSource.java | 51 +++++ src/main/java/com/example/model/Account.java | 20 ++ .../java/com/example/model/MoonMission.java | 22 ++ .../example/repository/AccountRepository.java | 16 ++ .../repository/JdbcAccountRepository.java | 104 +++++++++ .../repository/JdbcMoonMissionRepository.java | 94 ++++++++ .../repository/MoonMissionRepository.java | 15 ++ 8 files changed, 515 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/example/datasource/SimpleDriverManagerDataSource.java create mode 100644 src/main/java/com/example/model/Account.java create mode 100644 src/main/java/com/example/model/MoonMission.java create mode 100644 src/main/java/com/example/repository/AccountRepository.java create mode 100644 src/main/java/com/example/repository/JdbcAccountRepository.java create mode 100644 src/main/java/com/example/repository/JdbcMoonMissionRepository.java create mode 100644 src/main/java/com/example/repository/MoonMissionRepository.java diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6dc6fbd..323629f 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -1,36 +1,222 @@ package com.example; +import com.example.datasource.SimpleDriverManagerDataSource; +import com.example.model.Account; +import com.example.model.MoonMission; +import com.example.repository.AccountRepository; +import com.example.repository.JdbcAccountRepository; +import com.example.repository.JdbcMoonMissionRepository; +import com.example.repository.MoonMissionRepository; +import org.testcontainers.shaded.org.checkerframework.checker.units.qual.A; + +import javax.sql.DataSource; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.sql.SQLOutput; import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Scanner; public class Main { + private final AccountRepository accountRepository; + private final MoonMissionRepository missionRepository; + private final Scanner scanner; + + public Main(AccountRepository accountRepository, MoonMissionRepository missionRepository) { + this.accountRepository = accountRepository; + this.missionRepository = missionRepository; + this.scanner = new Scanner(System.in); + } + public static void main(String[] args) { - 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 + public Main() { 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."); + "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS \" +\n" + + "\"as system properties (-Dkey=value) or environment variables.\""); } try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { + System.out.println("✓ Database connection successful!"); } catch (SQLException e) { - throw new RuntimeException(e); + throw new RuntimeException("Failed to connect to database. Check your configuration.", e); + } + + DataSource dataSource = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass); + + this.accountRepository = new JdbcAccountRepository(dataSource); + this.missionRepository = new JdbcMoonMissionRepository(dataSource); + this.scanner = new Scanner(System.in); + } + + public void run() { + System.out.println("=== Moon Mission Database === \n"); + + if (!login()) { + System.out.println("Login invalid. Exiting..."); + return; + } + boolean running = true; + while (running) { + printMenu(); + int choice = getIntInput(); + + switch (choice) { + case 1 -> listMoonMissions(); + case 2 -> getMissionById(); + case 3 -> countMissionsByYear(); + case 4 -> createAccount(); + case 5 -> updateAccountPassword(); + case 6 -> deleteAccount(); + case 0 -> { + running = false; + System.out.println("Goodbye!"); + } + default -> System.out.println("Invalid option!"); + } + } + scanner.close(); + } + + private boolean login() { + System.out.print("Username: "); + String username = scanner.nextLine().trim(); + + System.out.print("Password: "); + String password = scanner.nextLine().trim(); + + Optional accountOpt = accountRepository.findByUsernameAndPassword(username, password); + + if (accountOpt.isPresent()) { + Account account = accountOpt.get(); + System.out.println("✓ Login successful! Welcome, " + account.name() + "\n"); + return true; + } else { + System.out.println("Invalid username or password."); + return false; + } + } + + private void printMenu() { + System.out.println("\n=== 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 option: "); + } + + private void listMoonMissions () { + System.out.println("\n --- Moon Missions ---"); + + List spacecrafts = missionRepository.findAllSpaceCraftNames(); + spacecrafts.forEach(System.out::println); + } + + private void getMissionById() { + System.out.print("Enter mission_id: "); + int missionId = getIntInput(); + + Optional missionOpt = missionRepository.findById(missionId); + + if (missionOpt.isPresent()) { + MoonMission mission = missionOpt.get(); + System.out.println("\n--- Mission Details ---"); + System.out.println("Mission ID: " + mission.missionId()); + System.out.println("Spacecraft: " + mission.spacecraft()); + System.out.println("Launch Date: " + mission.launchDate()); + System.out.println("Carrier Rocket: " + mission.carrierRocket()); + System.out.println("Operator: " + mission.operator()); + System.out.println("Mission Type: " + mission.missionType()); + System.out.println("Outcome: " + mission.outcome()); + } else { + System.out.println("Mission not found."); + } + } + + private void countMissionsByYear() { + System.out.print("Enter year: "); + int year = getIntInput(); + + int count = missionRepository.countByYear(year); + System.out.println("Missions launched in " + year + ": " + count); + } + + private void createAccount() { + System.out.print("First name: "); + String firstName = scanner.nextLine().trim(); + + System.out.print("Last name: "); + String lastName = scanner.nextLine().trim(); + + System.out.print("SSN: "); + String ssn = scanner.nextLine().trim(); + + System.out.print("Password: "); + String password = scanner.nextLine().trim(); + + int userId = accountRepository.create(firstName, lastName, ssn, password); + + if (userId > 0) { + System.out.println("✓ Account created successfully! User ID: " + userId); + } else { + System.out.println("Failed to create account."); + } + } + + private void updateAccountPassword() { + System.out.print("Enter user_id: "); + int userId = getIntInput(); + + System.out.print("Enter new password: "); + String newPassword = scanner.nextLine().trim(); + + boolean success = accountRepository.updatePassword(userId, newPassword); + + if (success) { + System.out.println("✓ Password updated successfully!"); + } else { + System.out.println("User not found."); + } + } + + private void deleteAccount() { + System.out.print("Enter user_id: "); + int userId = getIntInput(); + + boolean success = accountRepository.delete(userId); + + if (success) { + System.out.println("✓ Account deleted successfully!"); + } else { + System.out.println("User not found."); + } + } + + private int getIntInput() { + while(true) { + try { + String input = scanner.nextLine().trim(); + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("Invalid number. Try again: "); + } } - //Todo: Starting point for your code } /** diff --git a/src/main/java/com/example/datasource/SimpleDriverManagerDataSource.java b/src/main/java/com/example/datasource/SimpleDriverManagerDataSource.java new file mode 100644 index 0000000..92fee69 --- /dev/null +++ b/src/main/java/com/example/datasource/SimpleDriverManagerDataSource.java @@ -0,0 +1,51 @@ +package com.example.datasource; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.logging.Logger; + +public class SimpleDriverManagerDataSource implements DataSource { + + private final String url; + private final String username; + private final String password; + + public SimpleDriverManagerDataSource(String url, String username, String password) { + this.url = url; + this.username = username; + this.password = password; + } + + @Override + public Connection getConnection() throws SQLException { + return DriverManager.getConnection(url, username, password); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return DriverManager.getConnection(url, username, password); + } + + @Override + public java.io.PrintWriter getLogWriter() { return null; } + + @Override + public void setLogWriter(java.io.PrintWriter out) {} + + @Override + public void setLoginTimeout(int seconds) {} + + @Override + public int getLoginTimeout() { return 0; } + + @Override + public Logger getParentLogger() { return null; } + + @Override + public T unwrap(Class iface) { return null; } + + @Override + public boolean isWrapperFor(Class iface) { return false; } +} diff --git a/src/main/java/com/example/model/Account.java b/src/main/java/com/example/model/Account.java new file mode 100644 index 0000000..50f947e --- /dev/null +++ b/src/main/java/com/example/model/Account.java @@ -0,0 +1,20 @@ +package com.example.model; + +public record Account( + int userID, + String name, + String ssn, + String password +) { + public Account { + if(name == null || name.isBlank()) { + throw new IllegalArgumentException("Name cannot be blank"); + } + if (ssn == null || ssn.isBlank()) { + throw new IllegalArgumentException("SSN cannot be blank"); + } + if (password == null || password.isBlank()) { + throw new IllegalArgumentException("Password cannot be blank"); + } + } +} diff --git a/src/main/java/com/example/model/MoonMission.java b/src/main/java/com/example/model/MoonMission.java new file mode 100644 index 0000000..b20d55c --- /dev/null +++ b/src/main/java/com/example/model/MoonMission.java @@ -0,0 +1,22 @@ +package com.example.model; + +import java.time.LocalDate; + +public record MoonMission( + int missionId, + String spacecraft, + LocalDate launchDate, + String carrierRocket, + String operator, + String missionType, + String outcome +) { + public MoonMission { + if (spacecraft == null || spacecraft.isBlank()) { + throw new IllegalArgumentException("Spacecraft cannot be blank"); + } + if (launchDate == null) { + throw new IllegalArgumentException("Launch date cannot be null"); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/AccountRepository.java b/src/main/java/com/example/repository/AccountRepository.java new file mode 100644 index 0000000..cf03420 --- /dev/null +++ b/src/main/java/com/example/repository/AccountRepository.java @@ -0,0 +1,16 @@ +package com.example.repository; + +import com.example.model.Account; + +import java.util.Optional; + +public interface AccountRepository { + + Optional findByUsernameAndPassword (String username, String password); + + int create (String firstName, String lastName, String ssn, String password); + + boolean updatePassword(int userId, String newPassword); + + boolean delete(int userId); +} diff --git a/src/main/java/com/example/repository/JdbcAccountRepository.java b/src/main/java/com/example/repository/JdbcAccountRepository.java new file mode 100644 index 0000000..f911378 --- /dev/null +++ b/src/main/java/com/example/repository/JdbcAccountRepository.java @@ -0,0 +1,104 @@ +package com.example.repository; + +import com.example.model.Account; + +import javax.sql.DataSource; +import java.sql.*; +import java.util.Optional; + +public class JdbcAccountRepository implements AccountRepository { + + private final DataSource dataSource; + + public JdbcAccountRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public Optional findByUsernameAndPassword(String username, String password) { + String query = "SELECT user_id, name, ssn, password FROM account WHERE name = ? AND password = ?"; + + try(Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + + stmt.setString(1, username); + stmt.setString(2, password); + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + + Account account = new Account( + rs.getInt("user_id"), + rs.getString("name"), + rs.getString("ssn"), + rs.getString("password") + ); + return Optional.of(account); + } + } + } catch (SQLException e) { + throw new RuntimeException("Database error during login", e); + } + return Optional.empty(); + } + + @Override + public int create(String firstName, String lastName, String ssn, String password) { + + String username = firstName + lastName; + String query = "INSERT INTO account (name, ssn, password) VALUES (?, ?, ?)"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) { + + stmt.setString(1, username); + stmt.setString(2, ssn); + stmt.setString(3, password); + + stmt.executeUpdate(); + + try (ResultSet rs = stmt.getGeneratedKeys()) { + if (rs.next()) { + return rs.getInt(1); + } + } + } catch (SQLException e) { + throw new RuntimeException("Error creating account", e); + } + return -1; + } + + @Override + public boolean updatePassword(int userId, String newPassword) { + String query = "UPDATE account SET password = ? WHERE user_id = ?"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + + stmt.setString(1, newPassword); + stmt.setInt(2, userId); + + int rowsAffected = stmt.executeUpdate(); + return rowsAffected > 0; + + } catch (SQLException e) { + throw new RuntimeException("Error updating password", e); + } + } + + @Override + public boolean delete(int userId) { + String query = "DELETE FROM account WHERE user_id = ?"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + + stmt.setInt(1, userId); + + int rowsAffected = stmt.executeUpdate(); + return rowsAffected > 0; + } catch (SQLException e) { + throw new RuntimeException("Error deleting account", e); + } + } +} diff --git a/src/main/java/com/example/repository/JdbcMoonMissionRepository.java b/src/main/java/com/example/repository/JdbcMoonMissionRepository.java new file mode 100644 index 0000000..9c6d0b8 --- /dev/null +++ b/src/main/java/com/example/repository/JdbcMoonMissionRepository.java @@ -0,0 +1,94 @@ +package com.example.repository; + +import com.example.model.MoonMission; +import org.testcontainers.shaded.org.checkerframework.checker.units.qual.A; + +import javax.sql.DataSource; +import javax.swing.text.html.Option; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class JdbcMoonMissionRepository implements MoonMissionRepository { + + private final DataSource dataSource; + + public JdbcMoonMissionRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public List findAllSpaceCraftNames() { + List spacecrafts = new ArrayList<>(); + String query = "SELECT spacecraft FROM moon_mission"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ResultSet rs = stmt.executeQuery()) { + + while(rs.next()) { + spacecrafts.add(rs.getString("spacecraft")); + } + } catch (SQLException e) { + throw new RuntimeException("Error listing missions", e); + } + return spacecrafts; + } + + @Override + public Optional findById(int missionId) { + String query = "SELECT mission_id, spacecraft, launch_date, carrier_rocket, " + + "operator, mission_type, outcome " + + "FROM moon_mission WHERE mission_id = ?"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + + stmt.setInt(1, missionId); + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + MoonMission mission = new MoonMission( + rs.getInt("mission_id"), + rs.getString("spacecraft"), + rs.getDate("launch_date").toLocalDate(), + rs.getString("carrier_rocket"), + rs.getString("operator"), + rs.getString("mission_type"), + rs.getString("outcome") + ); + return Optional.of(mission); + } + } + + } catch (SQLException e) { + throw new RuntimeException("Error getting mission", e); + } + + return Optional.empty(); + } + + @Override + public int countByYear(int year) { + String query = "SELECT COUNT(*) as count FROM moon_mission WHERE YEAR(launch_date) = ?"; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + + stmt.setInt(1, year); + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return rs.getInt("count"); + } + } + } catch (SQLException e) { + throw new RuntimeException("Error counting missions", e); + } + return 0; + } +} diff --git a/src/main/java/com/example/repository/MoonMissionRepository.java b/src/main/java/com/example/repository/MoonMissionRepository.java new file mode 100644 index 0000000..22ccd78 --- /dev/null +++ b/src/main/java/com/example/repository/MoonMissionRepository.java @@ -0,0 +1,15 @@ +package com.example.repository; + +import com.example.model.MoonMission; + +import java.util.List; +import java.util.Optional; + +public interface MoonMissionRepository { + + List findAllSpaceCraftNames (); + + Optional findById(int missionId); + + int countByYear(int year); +} From 62702583d22b0b7d2be875e75f1bc6ce49601e5f Mon Sep 17 00:00:00 2001 From: Younes Date: Wed, 10 Dec 2025 18:29:26 +0100 Subject: [PATCH 3/3] Klar --- src/main/java/com/example/DevDatabaseInitializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/example/DevDatabaseInitializer.java b/src/main/java/com/example/DevDatabaseInitializer.java index e8a45fe..c17cb59 100644 --- a/src/main/java/com/example/DevDatabaseInitializer.java +++ b/src/main/java/com/example/DevDatabaseInitializer.java @@ -22,3 +22,4 @@ public static void start() { } } } +// klar \ No newline at end of file