diff --git a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java index 073fd81..f357ae1 100644 --- a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java +++ b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java @@ -1,5 +1,6 @@ package edu.group5.app.model.wrapper; +import edu.group5.app.utils.ParameterValidator; import java.math.BigDecimal; import java.sql.Connection; import java.sql.DriverManager; @@ -8,13 +9,13 @@ 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; +/** + * A class for wrapping the database. + */ public class DbWrapper { protected Connection connection; private static final String CONNECTION_TYPE = "jdbc:h2:"; @@ -24,6 +25,11 @@ public class DbWrapper { private List donations; private Logger logger = Logger.getLogger(DbWrapper.class.getName()); + /** + * The constructor, which constructs a String for connecting to the database. + * + * @param test Whether to construct the connection String for testing (in-memory) or not. + */ public DbWrapper(boolean test) { if (test) { this.connectionString = CONNECTION_TYPE + "mem:test;" + DB_SCRIPT + "test_init.sql'"; @@ -33,6 +39,11 @@ public DbWrapper(boolean test) { this.logger.info("connectionString constructed"); } + /** + * Connects to the database, and returns the result, logging failures. + * + * @return True if successful, false if not. + */ public boolean connect() { try { this.connection = DriverManager.getConnection(this.connectionString); @@ -49,8 +60,14 @@ public boolean connect() { } } + /** + * Disconnects the database connection, logging failures. + * + * @return True if successful, false if not. + */ public boolean disconnect() { - try{ this.connection.close(); } catch (Exception e) {}; + // We are not interested in whether it fails to close, as we check its closed status later. + try { this.connection.close(); } catch (Exception e) {}; try { return this.connection.isClosed(); } catch (Exception e) { @@ -59,16 +76,29 @@ public boolean disconnect() { } } + /** + * Closes queries and results. + * + * @param results The ResultSet to close, can be null. + * @param ps The PreparedStatement to close, can be null. + */ private void close(ResultSet results, PreparedStatement ps) { + // This method can take null arguments, so an exception is expected. try { results.close(); } catch (Exception e) {} try { ps.close(); } catch (Exception e) {} this.logger.info("results and ps closed"); } + /** + * Gets all users from the database. + * + * @return The users from the database returned as a List of Object arrays, where each Object + * array represents a user and a row in the users table in the database. + */ public List importUsers() { PreparedStatement ps = null; ResultSet results = null; - try{ + try { ps = this.connection.prepareStatement("SELECT * FROM users"); results = ps.executeQuery(); List data = new ArrayList(); @@ -93,37 +123,32 @@ public List importUsers() { return this.users; } - public int exportUsers(List data) { + /** + * Puts new users into the database. + * + * @param data The new users to put into the database. Each Object array in the List is a new + * user to add as a row. + * @return The number of rows affected in the transaction. + * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not + * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in + * the database, or any of the values in the rows can't be cast to the correct data-types. + */ + public int exportUsers(List data) throws IllegalArgumentException { this.importUsers(); - - if (data == null) { - throw new IllegalArgumentException("data can't be null"); - } - if (data.isEmpty()) { - return 0; - } - 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 ids = new HashSet<>(); - if (data.stream().anyMatch(i -> !ids.add(i[0]))) { - throw new IllegalArgumentException("data can't contain duplicate rows"); - } - + + ParameterValidator.exportChecker(data, "data", this.users, 6); 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 (?, ?, ?, ?, ?, ?)"); + """ + 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]); @@ -144,10 +169,21 @@ public int exportUsers(List data) { return rowsAffected; } + /** + * Imports all donations. + * + * @return A List of Object arrays for each donation in the database. + */ public List fetchAllDonations() { return this.importDonations(0, true); } + /** + * Imports the donations of a specific user based on a given user_id. + * + * @param user_id The id of the user to get the donations of. + * @return A List of Object arrays for each donation in the database. + */ public List importDonations(int user_id) { return this.importDonations(user_id, false); } @@ -155,7 +191,7 @@ public List importDonations(int user_id) { private List importDonations(int user_id, boolean all) { PreparedStatement ps = null; ResultSet results = null; - try{ + try { if (all) { ps = this.connection.prepareStatement("SELECT * FROM donations"); } else { @@ -185,34 +221,32 @@ private List importDonations(int user_id, boolean all) { return this.donations; } - public int exportDonations(List data) { + /** + * Puts new donations into the database. + * + * @param data The new donation to put into the database. Each Object array in the List is a new + * donations to add as a row. + * @return The number of rows affected in the transaction. + * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not + * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in + * the database, or any of the values in the rows can't be cast to the correct data-types. + */ + public int exportDonations(List data) throws IllegalArgumentException { this.fetchAllDonations(); - - if (data == null) { - throw new IllegalArgumentException("data can't be null"); - } - if (data.isEmpty()) { - return 0; - } - 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 ids = new HashSet<>(); - if (data.stream().anyMatch(i -> !ids.add(i[0]))) { - throw new IllegalArgumentException("data can't contain duplicate rows"); - } + + ParameterValidator.exportChecker(data, "data", this.donations, 6); 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 = ?), ?, ?, ?, ?)"); + """ + 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++) { diff --git a/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java b/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java index 910b110..2a493e4 100644 --- a/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java +++ b/src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java @@ -1,5 +1,6 @@ package edu.group5.app.model.wrapper; +import edu.group5.app.utils.ParameterValidator; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -24,11 +25,7 @@ public class OrgApiWrapper extends Wrapper { * @param urlString A string of the URL that's being connected to. */ public OrgApiWrapper(String urlString) { - if (urlString == null) { - throw new IllegalArgumentException("url can't be null"); - } else if (urlString.isBlank()) { - throw new IllegalArgumentException("url can't be blank"); - } + ParameterValidator.stringChecker(urlString, "url"); try { URI uri = URI.create(urlString); this.client = HttpClient.newHttpClient(); diff --git a/src/main/java/edu/group5/app/utils/ParameterValidator.java b/src/main/java/edu/group5/app/utils/ParameterValidator.java index 1a44e46..3bc3e5a 100644 --- a/src/main/java/edu/group5/app/utils/ParameterValidator.java +++ b/src/main/java/edu/group5/app/utils/ParameterValidator.java @@ -1,21 +1,28 @@ package edu.group5.app.utils; import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /** - * ParameterValidator is a utility class that provides static methods for validating various types of parameters. - * It includes methods for checking strings, integers, objects, and BigDecimal values to ensure they meet specific - * criteria such as not being null, not being blank, or being positive. - * + * ParameterValidator is a utility class that provides static methods for validating various types + * of parameters. + * It includes methods for checking strings, integers, objects, and BigDecimal values to ensure + * they meet specific criteria such as not being null, not being blank, or being positive. */ public final class ParameterValidator { /** * Validates that a string parameter is not null and not blank. + * * @param stringArg the string parameter to validate * @param variableName the name of the variable being validated, used in exception messages * @throws IllegalArgumentException if the string is null or blank */ - public static final void stringChecker(String stringArg, String variableName) throws IllegalArgumentException { + public static final void stringChecker(String stringArg, String variableName) + throws IllegalArgumentException { nullCheck(stringArg, variableName); if (stringArg.isBlank()) { throw new IllegalArgumentException(String.format("%s can't be blank", variableName)); @@ -24,33 +31,41 @@ public static final void stringChecker(String stringArg, String variableName) th /** * Validates that an integer parameter is not null and is a positive integer. + * * @param intArg the integer parameter to validate * @param variableName the name of the variable being validated, used in exception messages * @throws IllegalArgumentException if the integer is null or not a positive integer */ - public static final void intChecker(int intArg, String variableName) throws IllegalArgumentException { + public static final void intChecker(int intArg, String variableName) + throws IllegalArgumentException { if (intArg <= 0) { - throw new IllegalArgumentException(String.format("%s must be a positive integer", variableName)); + throw new IllegalArgumentException( + String.format("%s must be a positive integer", variableName) + ); } } /** * Validates that an object parameter is not null. + * * @param objectArg the object parameter to validate * @param variableName the name of the variable being validated, used in exception messages * @throws IllegalArgumentException if the object is null */ - public static final void objectChecker(Object objectArg, String variableName) throws IllegalArgumentException { + public static final void objectChecker(Object objectArg, String variableName) + throws IllegalArgumentException { nullCheck(objectArg, variableName); } /** * Validates that a BigDecimal parameter is not null and is greater than zero. + * * @param bigDecimalArg the BigDecimal parameter to validate * @param variableName the name of the variable being validated, used in exception messages * @throws IllegalArgumentException if the BigDecimal is null or not greater than zero */ - public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String variableName) throws IllegalArgumentException { + public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String variableName) + throws IllegalArgumentException { nullCheck(bigDecimalArg, variableName); if (bigDecimalArg.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException(String.format("%s must be larger than 0", variableName)); @@ -58,14 +73,55 @@ public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String vari } /** - * Helper method to check if a variable is null and throw an IllegalArgumentException with a formatted message if it is. + * Helper method to check if a variable is null and throw an IllegalArgumentException with a + * formatted message if it is. + * * @param variable the variable to check for null * @param variableName the name of the variable being checked, used in the exception message * @throws IllegalArgumentException if the variable is null */ - private static final void nullCheck(Object variable, String variableName) throws IllegalArgumentException { + private static final void nullCheck(Object variable, String variableName) + throws IllegalArgumentException { if (variable == null) { throw new IllegalArgumentException(String.format("%s can't be null", variableName)); } } + + /** + * A method for checking if the data to be exported is valid. + * + * @param data The data to export. + * @param dataName The name of the variable of the data to export. + * @param oldData The existing data to compare to for checking row existence. + * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not + * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in + * the database, or any of the values in the rows can't be cast to the correct data-types. + */ + public static final void exportChecker( + List data, String dataName, List oldData, int rowLength + ) throws IllegalArgumentException { + objectChecker(data, dataName); + if (!data.isEmpty()) { + if (data.stream().anyMatch(i -> i.length != rowLength)) { + throw new IllegalArgumentException( + String.format("%s's arrays must have a length of 6", dataName) + ); + } + if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) { + throw new IllegalArgumentException( + String.format("One or more rows in %s contains null values", dataName) + ); + } + + Set ids = new HashSet<>(); + if (data.stream().anyMatch(i -> !ids.add(i[0]))) { + throw new IllegalArgumentException("data can't contain duplicate rows"); + } + if (oldData.size() > 0) { + if (oldData.stream().anyMatch(i -> !ids.add(i[0]))) { + throw new IllegalArgumentException("data can't contain existing rows"); + } + } + } + } } diff --git a/src/main/java/edu/group5/app/utils/Utilities.java b/src/main/java/edu/group5/app/utils/Utilities.java deleted file mode 100644 index ce21d22..0000000 --- a/src/main/java/edu/group5/app/utils/Utilities.java +++ /dev/null @@ -1,5 +0,0 @@ -package edu.group5.app.utils; - -public class Utilities { - -} diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java index cf76092..1db7a90 100644 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java @@ -12,12 +12,12 @@ import java.util.Arrays; import java.util.Date; import java.util.List; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import edu.group5.app.model.wrapper.DbWrapper; - +/** + * A test class for interactions with the donations table using the DbWrapper. + */ public class DbWrapperDonationsTest { private Object[] johnDonation; private List users; @@ -54,13 +54,15 @@ void init() { users.add(row); } - this.johnDonation = new Object[] { 1, 1, 39, new BigDecimal(20.02), new Timestamp(new Date().getTime()), - "Paypal" }; + this.johnDonation = new Object[] { + 1, 1, 39, new BigDecimal(20.02), new Timestamp(new Date().getTime()), "Paypal" + }; this.donations = new ArrayList(); this.donations.add(this.johnDonation); - this.janeDonation = new Object[] { 2, 2, 39, new BigDecimal(20.00), new Timestamp(new Date().getTime()), - "Visa debit card" }; + this.janeDonation = new Object[] { + 2, 2, 39, new BigDecimal(20.00), new Timestamp(new Date().getTime()), "Visa debit card" + }; this.donations2 = new ArrayList(); this.donations2.add(this.johnDonation); this.donations2.add(this.janeDonation); @@ -104,7 +106,12 @@ public void importDonationsIsOnlyExportDonationsTest() { assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); assertTrue(this.db.exportDonations(this.donations) == 1); assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); - assertTrue(donationEquals(this.donations.get(0), this.db.importDonations((int) this.users.get(0)[0]).get(0))); + assertTrue( + donationEquals( + this.donations.get(0), + this.db.importDonations((int) this.users.get(0)[0]).get(0) + ) + ); assertTrue(this.db.disconnect()); }); } @@ -157,9 +164,12 @@ public void addingSameDonationTwiceThrowsExpectedException2() { assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 0); assertEquals(2, this.db.exportDonations(this.donations2)); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { - this.db.exportDonations(this.donations); - }); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> { + this.db.exportDonations(this.donations); + } + ); assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 1); assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 1); assertTrue(this.db.disconnect()); diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java index 7aca3c0..00107de 100644 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java @@ -2,27 +2,18 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.math.BigDecimal; -import java.util.Date; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.List; - -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import edu.group5.app.model.wrapper.DbWrapper; -import javafx.util.converter.BigDecimalStringConverter; - +/** + * A class for testing interactions with the users table of the database using DbWrapper. + */ public class DbWrapperUserTest { private Object[] johnDoe; private Object[] janeDoe; @@ -54,8 +45,9 @@ void init() { this.users3 = new ArrayList(); this.users3.add(this.janeDoe); - this.repeatingJoeJoe = new Object[] { 3, "Customer", "Repeating", "JoeJoe", "repeatingjjoe@email.com", - "passwordpassword" }; + this.repeatingJoeJoe = new Object[] { + 3, "Customer", "Repeating", "JoeJoe", "repeatingjjoe@email.com", "passwordpassword" + }; this.repeatedUsers = new ArrayList(); this.repeatedUsers.add(this.repeatingJoeJoe); this.repeatedUsers.add(this.repeatingJoeJoe); @@ -187,6 +179,4 @@ public void addingUserListWithNullInRowThrowsExpectedException() { assertTrue(this.db.disconnect()); assertEquals("One or more rows in data contains null values", exception.getMessage()); } - - } diff --git a/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java b/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java index 0a643f2..0165456 100644 --- a/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/OrgApiWrapperTest.java @@ -9,8 +9,6 @@ import java.lang.IllegalArgumentException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import edu.group5.app.model.wrapper.OrgApiWrapper; import tools.jackson.core.exc.StreamReadException; /** diff --git a/src/test/java/edu/group5/app/utils/UtilitiesTest.java b/src/test/java/edu/group5/app/utils/UtilitiesTest.java deleted file mode 100644 index 88aa0c9..0000000 --- a/src/test/java/edu/group5/app/utils/UtilitiesTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package edu.group5.app.utils; - -public class UtilitiesTest { - -}