Skip to content

Commit

Permalink
feat: merged feat/wrapper into workaround/ntnu-git-is-suboptimal
Browse files Browse the repository at this point in the history
  • Loading branch information
MatheaGjerde committed Mar 16, 2026
2 parents 06c135b + f3a6e5a commit 963d53f
Show file tree
Hide file tree
Showing 11 changed files with 704 additions and 42 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
<scope>runtime</scope>
</dependency>
</dependencies>

<build>
Expand Down
18 changes: 0 additions & 18 deletions src/main/java/edu/group5/app/App.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package edu.group5.app;

import java.util.HashMap;

import edu.group5.app.control.MainController;
import edu.group5.app.control.OrgApiWrapper;
import edu.group5.app.view.MainView;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;

/**
* Hello world!
Expand All @@ -30,17 +25,4 @@ public void start(Stage stage) {
stage.setScene(scene);
stage.show();
}

static void main(String[] args) throws InterruptedException {
OrgApiWrapper orgWrap = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all");
System.out.println();
System.out.println();
orgWrap.importData();
Object[] imports = orgWrap.getData();
ObjectMapper objectMapper = new ObjectMapper();
HashMap<String, Object> map = objectMapper.convertValue(imports[0], new TypeReference<HashMap<String, Object>>() {
});
System.out.println(map.get("org_number"));
launch(args);
}
}
232 changes: 232 additions & 0 deletions src/main/java/edu/group5/app/control/wrapper/DbWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package edu.group5.app.control.wrapper;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DbWrapper {
protected Connection connection;
private static final String CONNECTION_TYPE = "jdbc:h2:";
private static final String DB_SCRIPT = "INIT=RUNSCRIPT FROM 'classpath:";
private String connectionString;
private List<Object[]> users;
private List<Object[]> donations;
private Logger logger = Logger.getLogger(DbWrapper.class.getName());

public DbWrapper(boolean test) {
if (test) {
this.connectionString = CONNECTION_TYPE + "mem:test;" + DB_SCRIPT + "test_init.sql'";
} else {
this.connectionString = CONNECTION_TYPE + "file:./help-me-help;" + DB_SCRIPT + "init.sql'";
}
this.logger.info("connectionString constructed");
}

public boolean connect() {
try {
this.connection = DriverManager.getConnection(this.connectionString);
if (this.connection.isValid(0)) {
this.logger.info("Database connected");
return true;
} else {
this.logger.warning("Failed to connect to database");
return false;
}
} catch (SQLException e) {
this.logger.log(Level.SEVERE, "Failed to connect to database due to exception", e);
return false;
}
}

public boolean disconnect() {
try{ this.connection.close(); } catch (Exception e) {};
try {
return this.connection.isClosed();
} catch (Exception e) {
this.logger.log(Level.WARNING, "Failed to check if connection is closed due to exception", e);
return false;
}
}

private void close(ResultSet results, PreparedStatement ps) {
try { results.close(); } catch (Exception e) {}
try { ps.close(); } catch (Exception e) {}
this.logger.info("results and ps closed");
}

public List<Object[]> importUsers() {
PreparedStatement ps = null;
ResultSet results = null;
try{
ps = this.connection.prepareStatement("SELECT * FROM users");
results = ps.executeQuery();
List<Object[]> data = new ArrayList<Object[]>();
while (results.next()) {
data.add(
new Object[] {
results.getInt("user_id"),
results.getString("role"),
results.getString("first_name"),
results.getString("last_name"),
results.getString("email"),
results.getString("password_hash")
});
}
this.users = data;
this.logger.info("Users imported");
} catch (SQLException e) {
this.logger.log(Level.SEVERE, "Unexpected SQL exception", e);
} finally {
this.close(results, ps);
}
return this.users;
}

public int exportUsers(List<Object[]> data) {
this.importUsers();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) {
throw new IllegalArgumentException("One or more rows in data contains null values");
}
if (this.users.size() > 0) {
if ((int) data.getLast()[0] <= (int) this.users.getLast()[0]) {
throw new IllegalArgumentException("data can't contain existing rows");
}
}
Set<Object> ids = new HashSet<>();
if (data.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain duplicate rows");
}


PreparedStatement ps = null;
int rowsAffected = 0;
try {
ps = this.connection.prepareStatement(
"INSERT INTO users (user_id, role, first_name, last_name, email, password_hash) VALUES (?, ?, ?, ?, ?, ?)");
for (Object[] row : data) {
try {
ps.setInt(1, (int) row[0]);
for (int i = 1; i < row.length; i++) {
ps.setString(i + 1, (String) row[i]);
}
} catch (Exception e) {
throw new IllegalArgumentException("One or more rows in data contains a wrong datatype");
}
rowsAffected += ps.executeUpdate();
}
this.logger.info("Users exported");
} catch (SQLException e) {
this.logger.log(Level.SEVERE, "Unexpected SQL exception", e);
} finally {
this.close(null, ps);
}
return rowsAffected;
}

private List<Object[]> importDonations() {
return this.importDonations(0, true);
}

public List<Object[]> importDonations(int user_id) {
return this.importDonations(user_id, false);
}

private List<Object[]> importDonations(int user_id, boolean all) {
PreparedStatement ps = null;
ResultSet results = null;
try{
if (all) {
ps = this.connection.prepareStatement("SELECT * FROM donations");
} else {
ps = this.connection.prepareStatement("SELECT * FROM donations WHERE user_id = ?");
ps.setInt(1, user_id);
}
results = ps.executeQuery();
List<Object[]> data = new ArrayList<Object[]>();
while (results.next()) {
data.add(
new Object[] {
results.getInt(1),
results.getInt(2),
results.getInt(3),
results.getBigDecimal(4),
results.getTimestamp(5),
results.getString(6)
});
}
this.donations = data;
this.logger.info("Donations imported");
} catch (SQLException e) {
this.logger.log(Level.SEVERE, "Unexpected SQL exception", e);
} finally {
this.close(results, ps);
}
return this.donations;
}

public int exportDonations(List<Object[]> data) {
this.importDonations();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) {
throw new IllegalArgumentException("One or more rows in data contains null values");
}
if (this.donations.size() > 0 && (int) data.getLast()[0] <= (int) this.donations.getLast()[0]) {
throw new IllegalArgumentException("data can't contain existing rows");
}
Set<Object> ids = new HashSet<>();
if (data.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain duplicate rows");
}

PreparedStatement ps = null;
int rowsAffected = 0;
try {
ps = this.connection.prepareStatement(
"INSERT INTO donations (donation_id, user_id, organization_id, amount, dating, payment_method) VALUES (?, (SELECT user_id FROM users WHERE user_id = ?), ?, ?, ?, ?)");
for (Object[] row : data) {
try {
for (int i = 0; i < 3; i++) {
ps.setInt(i + 1, (int) row[i]);
}
ps.setBigDecimal(4, (BigDecimal) row[3]);
ps.setTimestamp(5, (Timestamp) row[4]);
ps.setString(6, (String) row[5]);
} catch (Exception e) {
throw new IllegalArgumentException("One or more rows in data contains a wrong datatype");
}
rowsAffected += ps.executeUpdate();
}
this.logger.info("Donations exported");
} catch (SQLException e) {
this.logger.log(Level.SEVERE, "Unexpected SQL exception", e);
} finally {
this.close(null, ps);
}
return rowsAffected;
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package edu.group5.app.control;
package edu.group5.app.control.wrapper;

import java.io.IOException;
import java.net.URI;
Expand All @@ -17,7 +17,8 @@ public class OrgApiWrapper extends Wrapper {
private HttpRequest request;

/**
* The constructor, which takes a url String and constructs a URI and HttpRequest object from it.
* The constructor, which takes a url String and constructs a URI and
* HttpRequest object from it.
* If the url is invalid, it will throw a fitting exception.
*
* @param urlString A string of the URL that's being connected to.
Expand All @@ -44,11 +45,13 @@ public OrgApiWrapper(String urlString) {
/**
* A method for importing data from the wrapped API.
*
* @return Returns a boolean, which indicates if the import was successful. Will be False if, for
* example, there is no internet connection.
* @return Returns a boolean, which indicates if the import was successful. Will
* be False if, for
* example, there is no internet connection.
*
* @throws InterruptedException This exception is thrown whenever the program is interrupted, like
* by ctrl + c.
* @throws InterruptedException This exception is thrown whenever the program is
* interrupted, like
* by ctrl + c.
*/
@Override
public boolean importData() throws InterruptedException {
Expand All @@ -67,7 +70,8 @@ public boolean importData() throws InterruptedException {
/**
* A method for accessing the imported data.
*
* @return Returns an array with HashMaps, which is how data is structured in the API.
* @return Returns an array with HashMaps, which is how data is structured in
* the API.
*/
@Override
public Object[] getData() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package edu.group5.app.control;
package edu.group5.app.control.wrapper;

/**
* An abstract class for all Wrappers of datasets.
Expand All @@ -9,13 +9,16 @@ protected Wrapper() {
}

/**
* An abstract method for importing data from the dataset that child methods wrap.
* An abstract method for importing data from the dataset that child methods
* wrap.
*
* @return Returns a boolean, which indicates if the import was successful. Will be False if, for
* example, there is no internet connection.
* @return Returns a boolean, which indicates if the import was successful. Will
* be False if, for
* example, there is no internet connection.
*
* @throws InterruptedException This exception is thrown whenever the program is interrupted, like
* by ctrl + c.
* @throws InterruptedException This exception is thrown whenever the program is
* interrupted, like
* by ctrl + c.
*/
public abstract boolean importData() throws InterruptedException;

Expand Down
23 changes: 23 additions & 0 deletions src/main/resources/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
CREATE TABLE IF NOT EXISTS
users (
user_id INT PRIMARY KEY,
role VARCHAR(32) NOT NULL,
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
email VARCHAR(32) NOT NULL,
password_hash VARCHAR(72) NOT NULL
);

CREATE TABLE IF NOT EXISTS
donations (
donation_id INT PRIMARY KEY,
user_id INT NOT NULL,
organization_id INT NOT NULL,
amount DECIMAL(32, 16) NOT NULL,
dating TIMESTAMP NOT NULL,
payment_method VARCHAR(32) NOT NULL,
CONSTRAINT fk_user
FOREIGN KEY (user_id)
REFERENCES users(user_id)
);

Loading

0 comments on commit 963d53f

Please sign in to comment.