Skip to content

Merge Feat/user into release/v1.0.0, completing the backend of the application #45

Merged
merged 22 commits into from
Mar 10, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e4b38a5
perf[User]: Improve User validation and update corresponding JUnit tests
Feb 28, 2026
0db6023
Perf: Update model classes and write Customer class
Mar 2, 2026
7c90b5b
perf: Add exception handling to increase robust code
Mar 2, 2026
02c60a5
Update: update imports to Customer class
Mar 2, 2026
3335ba2
fix: fix logic when adding preferance
Mar 3, 2026
c4b2eac
update: update relationship between Customer and intermideary Donatio…
Mar 3, 2026
6b313cc
update[Customer]: update customer class to better suit the application
Mar 3, 2026
a8f6f9f
fix[DonationService]: Change timestamp syntax to better readability a…
Mar 3, 2026
2e3bee8
Merge branch 'release/v1.0.0' into feat/User
Mar 3, 2026
c334f4e
merge: merge feat/wrapper into feat/user
Mar 3, 2026
dcc32bf
perf: increase performance of Organizationrepository
Mar 3, 2026
adce116
fix: fix Organizational classes by fixing constructor and JUnit tests
Mar 4, 2026
07dd8af
merge release/v1.0.0 into feat/User
Mar 4, 2026
f81c347
fix: fix login method's unnecessary String casting of char[] argument
Mar 5, 2026
be3995b
style[OrganizationRepository]: change style on method for better read…
Mar 5, 2026
fb67556
Fix: merge release/v1.0.0 into feat/User for finalization of the backend
Mar 5, 2026
6c34cfc
Fix: Fix OrganizationRepository constructor and update related classes
Mar 5, 2026
4ac5cdd
perf: unify repository constructors, unify Backend classes, standardi…
Mar 7, 2026
3f13cfa
Merge branch 'release/v1.0.0' into feat/User
Mar 7, 2026
c3b7e40
feat: merge release/v1.0.0 into feat/User, fix up on differences in b…
Mar 7, 2026
a875a55
feat[DonationRepository]: Add filterByUser() method and respective JU…
Mar 7, 2026
6982dd9
Update&Test[Repository]: Update Repository and JUnit tests
Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
<classifier>win</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
<classifier>win</classifier>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-crypto -->
<dependency>
Expand All @@ -45,13 +52,17 @@
<version>3.1.0</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>

<build>
<plugins>
Expand Down Expand Up @@ -112,6 +123,19 @@
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/NOTICE</exclude>
<exclude>META-INF/LICENSE</exclude>
<exclude>META-INF.versions.9.module-info</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>edu.group5.app.App</mainClass>
Expand Down
9 changes: 1 addition & 8 deletions src/main/java/edu/group5/app/App.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package edu.group5.app;

import java.sql.Wrapper;
import java.util.HashMap;

import edu.group5.app.control.OrgAPIWrapper;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;

/**
* Hello world!
*/
public class App {
public static void main(String[] args) throws InterruptedException {
Wrapper wrap = new Wrapper();
System.out.println("Hello World!");
}
}
22 changes: 19 additions & 3 deletions src/main/java/edu/group5/app/model/DBRepository.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package edu.group5.app.model;


import java.util.HashMap;
import java.util.Map;

import java.util.List;
/**
* Abstract base class for repositories that store their data
* in a database-related structure.
Expand All @@ -14,12 +12,30 @@
* </p>
*/
public abstract class DBRepository<K, V> extends Repository<K, V> {
protected final Map<K, V> contentLock;
/**
* Constructs a DBRepository with the given content.
*
* @param content the HashMap used to store repository entities
*/
protected DBRepository(Map<K, V> content) {
super(content);
this.contentLock = new HashMap<>();
}

protected void updateContentLock() {
synchronized (contentLock) {
contentLock.clear();
contentLock.putAll(this.content);
}
}

public abstract boolean addContent(V value);

/**
* Exports the repository content as a list of Object arrays, where each array represents a row of data.
* This method is intended for converting the repository content into a format suitable for database storage or export.
* @return a List of Object arrays representing the repository content for database export
*/
public abstract List<Object[]> export();
}
2 changes: 1 addition & 1 deletion src/main/java/edu/group5/app/model/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class Repository<K, V> {
*
* @param content the underlying data structure used to store entities
*/
public Repository(Map<K, V> content) {
protected Repository(Map<K, V> content) {
this.content = content;
}

Expand Down
103 changes: 90 additions & 13 deletions src/main/java/edu/group5/app/model/donation/DonationRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.List;
import java.math.BigDecimal;
import java.sql.Timestamp;

/**
* Repository class for Donation.
Expand All @@ -19,17 +22,70 @@ public class DonationRepository extends DBRepository<Integer, Donation> {
private final HashMap<Integer, Donation> content;

/**
* Constructs DonationRepository using Hashmap,
* and extends the content from DBRepository.
* @param content the underlying map used to store donations,
* where the key represents the donation ID
* Constructs DonationRepository from a list of Object[] rows from the database.
* @param rows List of Object[] representing donations from the DB.
* Each row must have 6 elements:
* [donationId, userId, organizationId, amount, date, paymentMethod]
* @throws IllegalArgumentException if the input list is null or any row is invalid
*/
public DonationRepository(HashMap<Integer, Donation> content){
if (content == null) {
throw new IllegalArgumentException("Content cannot be null");
public DonationRepository(List<Object[]> rows) {
super(new HashMap<>());
if (rows == null) {
throw new IllegalArgumentException("The list of rows cannot be null");
}
super(content);
this.content = content;
this.content = new HashMap<>();
for (Object[] row : rows) {
if (row == null || row.length != 6 ) {
throw new IllegalArgumentException("Each row must contain exactly 6 elements");
}
int donationId = (int) row[0];
int customerId = (int) row[1];
int organizationId = (int) row[2];
BigDecimal amount = (BigDecimal) row[3];
Timestamp date = (Timestamp) row[4];
String paymentMethod = (String) row[5];

Donation donation = new Donation(donationId, customerId, organizationId, amount, date, paymentMethod);
this.content.put(donationId, donation);
}
super.updateContentLock();
}

@Override
public List<Object[]> export() {
return content.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> { Donation donation = entry.getValue();
return new Object[] {
donation.donationId(), donation.userId(),
donation.organizationId(), donation.amount(),
donation.date(), donation.paymentMethod()};})
.toList();
}

/**
* Retrieves a donation by its ID.
* @param donationId the ID of the donation to retrieve
* @return the Donation object with the specified ID, or null if not found
* @throws IllegalArgumentException if the donationId is not positive
*/
public Donation getDonationById(int donationId) {
if (donationId <= 0) {
throw new IllegalArgumentException("Donation ID must be positive");
}
return content.get(donationId);
}

/**
* Generates the next donation ID based on the current maximum ID in the repository.
* @return the next donation ID to be used for a new donation
*/
public int getNextDonationId() {
return content.keySet().stream().max(Integer::compareTo).orElse(0) + 1;
} /* TODO change this when data database is introduced */

public Map<Integer, Donation> getAllDonations() {
return new HashMap<>(content);
}

/**
Expand All @@ -44,14 +100,15 @@ public DonationRepository(HashMap<Integer, Donation> content){
* @return {@code true} if the donation was successfully added, and
* {@code false} if a donation with the same ID already exists
*/
public boolean addDonation(Donation donation) {
@Override
public boolean addContent(Donation donation) {
if (donation == null) {
throw new IllegalArgumentException("Donation cannot be null");
}
if (content.containsKey(donation.donationId())){
return false;
return false;
}
content.put(donation.donationId(), donation);
this.content.put(donation.donationId(), donation);
return true;
}

Expand Down Expand Up @@ -98,9 +155,9 @@ public HashMap<Integer, Donation> sortByAmount() {

/**
* Returns all donations associated with a specific organization.
*
* @param orgNumber the organization ID to filter by
* @return a map containing all donations that belong to the given organization
* @throws IllegalArgumentException if the orgNumber is not positive
*/
public HashMap<Integer, Donation> filterByOrganization(int orgNumber) {
if (orgNumber <= 0) {
Expand All @@ -116,4 +173,24 @@ public HashMap<Integer, Donation> filterByOrganization(int orgNumber) {
LinkedHashMap::new
));
}

/**
* Returns all donations made by a specific user.
* @param userId the user ID to filter by
* @return a map containing all donations that belong to the given user
* @throws IllegalArgumentException if the userId is not positive
*/
public HashMap<Integer, Donation> filterByUser(int userId) {
if (userId <= 0) {
throw new IllegalArgumentException("User ID must be positive");
}
return content.entrySet().stream()
.filter(entry -> entry.getValue().userId() == userId)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
}
63 changes: 63 additions & 0 deletions src/main/java/edu/group5/app/model/donation/DonationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package edu.group5.app.model.donation;
import java.time.Instant;

import java.math.BigDecimal;
import java.sql.Timestamp;
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.organization.OrganizationRepository;
import edu.group5.app.model.user.Customer;

/**
* DonationService class provides functionality for handling donations in the system.
* It interacts with the DonationRepository to manage donation records
* and the OrganizationRepository to validate organization information.
* The donate method allows a customer to make a donation to a specified organization,
* ensuring that the customer, organization, and donation amount are valid before processing the donation.
*/
public class DonationService {

private final DonationRepository donationRepository;
private final OrganizationRepository organizationRepository;

/**
* Constructor for DonationService. Initializes the service with the required repositories.
* @param donationRepository the repository for managing donation records
* @param organizationRepository the repository for managing organization information
* @throws IllegalArgumentException if either repository is null
*/
public DonationService(DonationRepository donationRepository,
OrganizationRepository organizationRepository) {
if (donationRepository == null) {
throw new IllegalArgumentException("DonationRepository cannot be null");
}
if (organizationRepository == null) {
throw new IllegalArgumentException("OrganizationRepository cannot be null");
}
this.donationRepository = donationRepository;
this.organizationRepository = organizationRepository;
}

/**
* Processes a donation from a customer to a specified organization with a given amount.
* Validates the customer, organization number, and donation amount before creating a donation record.
* @param customer the customer making the donation
* @param orgNumber the organization number to which the donation is made
* @param amount the amount of the donation
* @return true if the donation is successfully processed, false otherwise
*/
public boolean donate(Customer customer, int orgNumber, BigDecimal amount, String paymentMethod) {
if (customer == null || amount == null
|| amount.compareTo(BigDecimal.ZERO) <= 0 || paymentMethod.isBlank()) {
return false;
}
Organization org = organizationRepository.findByOrgNumber(orgNumber);
if (org == null) {
return false;
}
Donation donation =
new Donation(donationRepository.getNextDonationId(),
customer.getUserId(), org.orgNumber(), amount, Timestamp.from(Instant.now()), paymentMethod);
donationRepository.addContent(donation);
return true;
}
}
Loading