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..017178d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,11 @@
${junit.jupiter.version}
test
+
+ org.slf4j
+ slf4j-nop
+ 2.0.17
+
org.assertj
assertj-core
diff --git a/src/main/java/com/example/Account.java b/src/main/java/com/example/Account.java
new file mode 100644
index 0000000..9d7fe06
--- /dev/null
+++ b/src/main/java/com/example/Account.java
@@ -0,0 +1,10 @@
+package com.example;
+
+public record Account(
+ int userId,
+ String name,
+ String password,
+ String firstName,
+ String lastName,
+ String ssn
+) {}
\ No newline at end of file
diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java
new file mode 100644
index 0000000..80b0231
--- /dev/null
+++ b/src/main/java/com/example/AccountRepository.java
@@ -0,0 +1,9 @@
+package com.example;
+import java.util.Optional;
+
+public interface AccountRepository {
+ Optional findByNameAndPassword(String name, String password);
+ Account create(Account account);
+ boolean updatePassword(int userId, String newPassword);
+ boolean delete(int userId);
+}
diff --git a/src/main/java/com/example/AccountRepositoryJdbc.java b/src/main/java/com/example/AccountRepositoryJdbc.java
new file mode 100644
index 0000000..ddb94dd
--- /dev/null
+++ b/src/main/java/com/example/AccountRepositoryJdbc.java
@@ -0,0 +1,90 @@
+package com.example;
+
+
+import com.example.SimpleDriverManagerDataSource;
+
+import java.sql.*;
+import java.util.Optional;
+
+public class AccountRepositoryJdbc implements AccountRepository {
+ private final SimpleDriverManagerDataSource ds;
+
+ public AccountRepositoryJdbc(SimpleDriverManagerDataSource ds) {
+ this.ds = ds;
+ }
+
+ @Override
+ public Optional findByNameAndPassword(String name, String password) {
+ String sql = "SELECT * FROM account WHERE name = ? AND password = ?";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setString(1, name);
+ ps.setString(2, password);
+ try (ResultSet rs = ps.executeQuery()) {
+ if (rs.next()) {
+ return Optional.of(new Account(
+ rs.getInt("user_id"),
+ rs.getString("name"),
+ rs.getString("password"),
+ rs.getString("first_name"),
+ rs.getString("last_name"),
+ rs.getString("ssn")
+ ));
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Account create(Account account) {
+ String sql = "INSERT INTO account (name, password, first_name, last_name, ssn) VALUES (?, ?, ?, ?, ?)";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ ps.setString(1, account.name());
+ ps.setString(2, account.password());
+ ps.setString(3, account.firstName());
+ ps.setString(4, account.lastName());
+ ps.setString(5, account.ssn());
+ ps.executeUpdate();
+ try (ResultSet keys = ps.getGeneratedKeys()) {
+ if (keys.next()) {
+ return new Account(keys.getInt(1), account.name(), account.password(),
+ account.firstName(), account.lastName(), account.ssn());
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return account;
+ }
+
+ @Override
+ public boolean updatePassword(int userId, String newPassword) {
+ String sql = "UPDATE account SET password = ? WHERE user_id = ?";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setString(1, newPassword);
+ ps.setInt(2, userId);
+ return ps.executeUpdate() > 0;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean delete(int userId) {
+ String sql = "DELETE FROM account WHERE user_id = ?";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setInt(1, userId);
+ return ps.executeUpdate() > 0;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/example/DevDatabaseInitializer.java b/src/main/java/com/example/DevDatabaseInitializer.java
index e8a45fe..b6323f0 100644
--- a/src/main/java/com/example/DevDatabaseInitializer.java
+++ b/src/main/java/com/example/DevDatabaseInitializer.java
@@ -16,9 +16,11 @@ public static void start() {
.withInitScript("init.sql");
mysql.start();
+
System.setProperty("APP_JDBC_URL", mysql.getJdbcUrl());
System.setProperty("APP_DB_USER", mysql.getUsername());
System.setProperty("APP_DB_PASS", mysql.getPassword());
+ System.out.println("Init script loaded, JDBC URL: " + mysql.getJdbcUrl());
}
}
}
diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java
index 6dc6fbd..5bc2b8b 100644
--- a/src/main/java/com/example/Main.java
+++ b/src/main/java/com/example/Main.java
@@ -1,13 +1,16 @@
package com.example;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
+import com.example.Account;
+import com.example.AccountRepository;
+import com.example.AccountRepositoryJdbc;
+import com.example.MoonMissionRepository;
+import com.example.MoonMissionRepositoryJdbc;
+
import java.util.Arrays;
+import java.util.Scanner;
public class Main {
-
- static void main(String[] args) {
+ public static void main(String[] args) {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
@@ -15,43 +18,113 @@ static void main(String[] args) {
}
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.");
+ throw new IllegalStateException("Missing DB configuration...");
}
- try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ SimpleDriverManagerDataSource ds = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass);
+ AccountRepository accountRepo = new AccountRepositoryJdbc(ds);
+ MoonMissionRepository missionRepo = new MoonMissionRepositoryJdbc(ds);
+
+ Scanner sc = new Scanner(System.in);
+
+ System.out.println("Username:");
+ String username = sc.nextLine();
+ System.out.println("Password:");
+ String password = sc.nextLine();
+
+ if (accountRepo.findByNameAndPassword(username, password).isPresent()) {
+ System.out.println("username accepted");
+ menuLoop(accountRepo, missionRepo, sc);
+ } else {
+ System.out.println("Invalid username or password");
+ System.out.println("0) Exit");
+ String opt = sc.nextLine();
+ if ("0".equals(opt)) {
+ return;
+ }
+ }
+ }
+
+ private void menuLoop(AccountRepository accountRepo, MoonMissionRepository missionRepo, Scanner sc) {
+ boolean running = true;
+ while (running) {
+ System.out.println("Menu:");
+ System.out.println("1) List moon missions");
+ System.out.println("2) Get mission by id");
+ System.out.println("3) Count missions by year");
+ System.out.println("4) Create account");
+ System.out.println("5) Update account password");
+ System.out.println("6) Delete account");
+ System.out.println("0) Exit");
+
+ String choice = sc.nextLine();
+ switch (choice) {
+ case "1":
+ missionRepo.findAll().forEach(m -> System.out.println(m.spacecraft()));
+ break;
+ case "2":
+ System.out.println("mission_id:");
+ int id = Integer.parseInt(sc.nextLine());
+ missionRepo.findById(id).ifPresentOrElse(
+ m -> System.out.println("Mission " + m.missionId() + ": " + m.spacecraft()),
+ () -> System.out.println("No mission found")
+ );
+ break;
+ case "3":
+ System.out.println("year:");
+ int year = Integer.parseInt(sc.nextLine());
+ int count = missionRepo.countByYear(year);
+ System.out.println(count + " missions in " + year);
+ break;
+ case "4":
+ System.out.println("first name:");
+ String fn = sc.nextLine();
+ System.out.println("last name:");
+ String ln = sc.nextLine();
+ System.out.println("ssn:");
+ String ssn = sc.nextLine();
+ System.out.println("password:");
+ String pw = sc.nextLine();
+ String name = fn.substring(0,3) + ln.substring(0,3);
+ accountRepo.create(new Account(0, name, pw, fn, ln, ssn));
+ System.out.println("account created");
+ break;
+ case "5":
+ System.out.println("user_id:");
+ int uid = Integer.parseInt(sc.nextLine());
+ System.out.println("new password:");
+ String newPw = sc.nextLine();
+ if (accountRepo.updatePassword(uid, newPw)) {
+ System.out.println("updated");
+ }
+ break;
+ case "6":
+ System.out.println("user_id:");
+ int delId = Integer.parseInt(sc.nextLine());
+ if (accountRepo.delete(delId)) {
+ System.out.println("deleted");
+ }
+ break;
+ case "0":
+ running = false;
+ break;
+ default:
+ System.out.println("Invalid option");
+ }
}
- //Todo: Starting point for your code
}
- /**
- * Determines if the application is running in development mode based on system properties,
- * environment variables, or command-line arguments.
- *
- * @param args an array of command-line arguments
- * @return {@code true} if the application is in development mode; {@code false} otherwise
- */
private static boolean isDevMode(String[] args) {
- if (Boolean.getBoolean("devMode")) //Add VM option -DdevMode=true
- return true;
- if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) //Environment variable DEV_MODE=true
- return true;
- return Arrays.asList(args).contains("--dev"); //Argument --dev
+ if (Boolean.getBoolean("devMode")) return true;
+ if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true;
+ return Arrays.asList(args).contains("--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()) {
diff --git a/src/main/java/com/example/MoonMission.java b/src/main/java/com/example/MoonMission.java
new file mode 100644
index 0000000..777204f
--- /dev/null
+++ b/src/main/java/com/example/MoonMission.java
@@ -0,0 +1,14 @@
+package com.example;
+
+import java.time.LocalDate;
+
+public record MoonMission(
+ int missionId,
+ String spacecraft,
+ LocalDate launchDate,
+ String carrierRocket,
+ String operator,
+ String missionType,
+ 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..f961d1a
--- /dev/null
+++ b/src/main/java/com/example/MoonMissionRepository.java
@@ -0,0 +1,11 @@
+package com.example;
+
+import com.example.MoonMission;
+import java.util.List;
+import java.util.Optional;
+
+public interface MoonMissionRepository {
+ List findAll();
+ Optional findById(int missionId);
+ int countByYear(int year);
+}
diff --git a/src/main/java/com/example/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/MoonMissionRepositoryJdbc.java
new file mode 100644
index 0000000..2a31b7f
--- /dev/null
+++ b/src/main/java/com/example/MoonMissionRepositoryJdbc.java
@@ -0,0 +1,76 @@
+package com.example;
+
+import java.sql.*;
+import java.util.*;
+
+public class MoonMissionRepositoryJdbc implements MoonMissionRepository {
+ private final SimpleDriverManagerDataSource ds;
+
+ public MoonMissionRepositoryJdbc(SimpleDriverManagerDataSource ds) {
+ this.ds = ds;
+ }
+
+ @Override
+ public List findAll() {
+ List missions = new ArrayList<>();
+ String sql = "SELECT * FROM moon_mission ORDER BY mission_id";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql);
+ ResultSet rs = ps.executeQuery()) {
+ while (rs.next()) {
+ missions.add(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")
+ ));
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return missions;
+ }
+
+ @Override
+ public Optional findById(int missionId) {
+ String sql = "SELECT * FROM moon_mission WHERE mission_id = ?";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setInt(1, missionId);
+ try (ResultSet rs = ps.executeQuery()) {
+ if (rs.next()) {
+ return Optional.of(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")
+ ));
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public int countByYear(int year) {
+ String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?";
+ try (Connection conn = ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setInt(1, year);
+ try (ResultSet rs = ps.executeQuery()) {
+ if (rs.next()) return rs.getInt(1);
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java
new file mode 100644
index 0000000..cdda3bf
--- /dev/null
+++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java
@@ -0,0 +1,21 @@
+package com.example;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class SimpleDriverManagerDataSource {
+ private final String url;
+ private final String user;
+ private final String pass;
+
+ public SimpleDriverManagerDataSource(String url, String user, String pass) {
+ this.url = url;
+ this.user = user;
+ this.pass = pass;
+ }
+
+ public Connection getConnection() throws SQLException {
+ return DriverManager.getConnection(url, user, pass);
+ }
+}
diff --git a/src/main/resources/init.sql b/src/main/resources/init.sql
index e4ffd06..fa390ed 100644
--- a/src/main/resources/init.sql
+++ b/src/main/resources/init.sql
@@ -27,7 +27,6 @@ CREATE TABLE moon_mission
);
--
-- Lägger till data i tabellerna för Laboration 1.
---
INSERT INTO account (password, first_name, last_name, ssn)
VALUES ('8j_]xrCfh#t5,vne', 'Alexandra', 'Truby', '930213-1480');
INSERT INTO account (password, first_name, last_name, ssn)