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));
+ }
+}
diff --git a/src/main/java/edu/group5/app/model/donation/DonationService.java b/src/main/java/edu/group5/app/model/donation/DonationService.java
new file mode 100644
index 0000000..690a632
--- /dev/null
+++ b/src/main/java/edu/group5/app/model/donation/DonationService.java
@@ -0,0 +1,81 @@
+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;
+ }
+
+ /**
+ * Getter for the DonationRepository used by this service.
+ * This method allows access to the donation repository for managing donation records and retrieving donation information.
+ * @return the DonationRepository instance used by this service
+ */
+ public DonationRepository getDonationRepository() {
+ return this.donationRepository;
+ }
+
+ /**
+ * Getter for the OrganizationRepository used by this service.
+ * This method allows access to the organization repository for validating organization information when processing donations.
+ * @return the OrganizationRepository instance used by this service
+ */
+ public OrganizationRepository getOrganizationRepository() {
+ return this.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);
+ this.donationRepository.addContent(donation);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/group5/app/model/organization/Organization.java b/src/main/java/edu/group5/app/model/organization/Organization.java
index 825945f..42844e1 100644
--- a/src/main/java/edu/group5/app/model/organization/Organization.java
+++ b/src/main/java/edu/group5/app/model/organization/Organization.java
@@ -1,5 +1,61 @@
package edu.group5.app.model.organization;
-public class Organization {
-
+import java.util.Objects;
+
+/**
+ * Represents an organization.
+ *
+ *
+ * An organization is identified by an organization number, a name,
+ * trust status, website Url, pre-approval status, and a textual description.
+ *
+ *
+ * Instances are validated on creation:
+ *
+ * - orgNumber must be non-negative
+ * - name and websiteUrl must not be null or blank
+ * - description must not be null
+ *
+ */
+public record Organization(
+ int orgNumber,
+ String name,
+ boolean trusted,
+ String websiteUrl,
+ boolean isPreApproved,
+ String description) {
+ /**
+ * Creates a new organization.
+ *
+ * @param orgNumber the organization number; must be non-negative
+ * @param name the organization name; must not be null or blank
+ * @param trusted whether the organization is trusted
+ * @param websiteUrl the organization's website Url; must not be null or
+ * blank
+ * @param isPreApproved whether the organization is pre-approved
+ * @param description a textual description of the organization; must not be
+ * null
+ * @throws NullPointerException if name, websiteUrl or description is null
+ * @throws IllegalArgumentException if orgNumber is negative, or if name or
+ * websiteUrl is blank
+ */
+ public Organization(int orgNumber, String name, boolean trusted, String websiteUrl, boolean isPreApproved,
+ String description) {
+ if (orgNumber < 0) {
+ throw new IllegalArgumentException("orgNumber cannot be negative");
+ }
+ this.orgNumber = orgNumber;
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ this.trusted = trusted;
+ this.websiteUrl = Objects.requireNonNull(websiteUrl, "websiteUrl cannot be null");
+ this.isPreApproved = isPreApproved;
+ this.description = Objects.requireNonNull(description, "description cannot be null");
+
+ if (name.isBlank()) {
+ throw new IllegalArgumentException("name cannot be blank");
+ }
+ if (websiteUrl.isBlank()) {
+ throw new IllegalArgumentException("websiteUrl cannot be blank");
+ }
+ }
}
diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java
new file mode 100644
index 0000000..a47b3d5
--- /dev/null
+++ b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java
@@ -0,0 +1,116 @@
+package edu.group5.app.model.organization;
+
+import edu.group5.app.model.Repository;
+import tools.jackson.core.type.TypeReference;
+import tools.jackson.databind.ObjectMapper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Repository class for managing Organization entities. It provides methods to retrieve trusted organizations,
+ * find organizations by their organization number or name, and initializes the repository with input data.
+ * The repository uses a HashMap to store Organization objects for efficient retrieval based on their organization number.
+ * Handles the business logic associated with organizations
+ */
+public class OrganizationRepository extends Repository {
+ private final HashMap grandMap;
+
+ /**
+ * Initializes the repository with the given input data, c
+ * onverting it into Organization objects and storing them in a map for efficient retrieval.
+ * The input is expected to be an array of objects, where each object contains
+ * the necessary information to create an Organization.
+ * @param input the input data used to populate the repository, must not be null
+ * @throws IllegalArgumentException if the input is null
+ */
+ public OrganizationRepository(Object[] input) {
+ super(new HashMap<>());
+ grandMap = new HashMap<>();
+ if (input == null) {
+ throw new IllegalArgumentException("The input cannot be null");
+ }
+ ObjectMapper mapper = new ObjectMapper();
+
+ for (Object obj : input) {
+ HashMap contentMap =
+ mapper.convertValue(obj, new TypeReference>() {});
+
+ String orgNumberStr = ((String) contentMap.get("org_number")).replaceAll("\\s", "");
+ int orgNumber = Integer.parseInt(orgNumberStr);
+ String name = (String) contentMap.get("name");
+ boolean trusted = "approved".equalsIgnoreCase((String) contentMap.get("status"));
+ String websiteURL = (String) contentMap.get("url");
+ boolean isPreApproved = Boolean.TRUE.equals(contentMap.get("is_pre_approved"));
+ String description = "Information about " + name;
+ Organization org = new Organization(orgNumber, name, trusted, websiteURL, isPreApproved, description);
+
+ grandMap.put(org.orgNumber(), org);
+ }
+ }
+
+ /**
+ * Exports the organization data in a format suitable for output, such as JSON. Each organization is represented as a map containing its attributes.
+ * @return an array of maps, where each map represents an organization with its attributes (org_number, name, status, url, is_pre_approved)
+ * @throws IllegalStateException if the repository is empty
+ */
+ public Object[] export() {
+ if (grandMap.isEmpty()) {
+ throw new IllegalStateException("The repository is empty");
+ }
+ return grandMap.values().stream()
+ .map(org -> { Map orgMap = new HashMap<>();
+ orgMap.put("org_number", org.orgNumber());
+ orgMap.put("name", org.name());
+ orgMap.put("status", org.trusted() ? "approved" : "unapproved");
+ orgMap.put("url", org.websiteUrl());
+ orgMap.put("is_pre_approved", org.isPreApproved());
+ return orgMap;
+ })
+ .toArray();
+ }
+
+/**
+ * Gets all trusted organizations in the repository
+ * @return all organizations with trusted = true
+ */
+ public Map getTrustedOrganizations() {
+ Map trustedOrganizations = new HashMap<>();
+
+ grandMap.forEach((orgNr, org) -> {
+ if (org.trusted()) {
+ trustedOrganizations.put(orgNr, org);
+ }
+ });
+ return trustedOrganizations;
+ }
+
+ /**
+ * Finds an organization by its organization number
+ * @param orgNumber the organization number of the Organization
+ * @return the Organization with the given organization number, or null if not found
+ * @throws IllegalArgumentException if the organization number is not a positive integer
+ */
+ public Organization findByOrgNumber(int orgNumber) {
+ if (orgNumber <= 0) {
+ throw new IllegalArgumentException("The Organization number must be a positive integer");
+ }
+ return grandMap.get(orgNumber);
+ }
+
+ /**
+ * Finds an organization by its name, ignoring case
+ * @param name the name of the Organization
+ * @return the Organization with the given name, or null if not found
+ * @throws IllegalArgumentException if the name is null or empty
+ */
+ public Organization findByOrgName(String name) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("The name cannot be null");
+ }
+ return grandMap.values().stream()
+ .filter(org -> org.name().equalsIgnoreCase(name))
+ .findFirst()
+ .orElse(null);
+ }
+}
diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
new file mode 100644
index 0000000..c5979f5
--- /dev/null
+++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java
@@ -0,0 +1,58 @@
+package edu.group5.app.model.organization;
+
+import java.util.Map;
+
+/**
+ * Service class for managing organization-related operations.
+ * It interacts with the OrganizationRepository to retrieve organization information
+ * and contains business logic associated with organization management.
+ */
+public class OrganizationService {
+ private OrganizationRepository organizationRepository;
+
+ /**
+ * Constructs an OrganizationService with the given OrganizationRepository.
+ * @param organizationRepository the OrganizationRepository to use for managing organization data; must not be null
+ * @throws IllegalArgumentException if organizationRepository is null
+ */
+ public OrganizationService(OrganizationRepository organizationRepository) {
+ if (organizationRepository == null) {
+ throw new IllegalArgumentException("OrganizationRepository cannot be null");
+ }
+ this.organizationRepository = organizationRepository;
+ }
+
+ /**
+ * Getter for the OrganizationRepository used by this service.
+ * @return the OrganizationRepository instance used by this service
+ */
+ public OrganizationRepository getOrganizationRepository() {
+ return this.organizationRepository;
+ }
+
+ /**
+ * Retrieves all trusted organizations.
+ * @return a map of trusted organizations by organization number
+ */
+ public Map getTrustedOrganizations() {
+ return organizationRepository.getTrustedOrganizations();
+ }
+
+ /**
+ * Finds an organization by its organization number.
+ * @param orgNumber the organization number to find
+ * @return the Organization if found, null otherwise
+ */
+ public Organization findByOrgNumber(int orgNumber) {
+ return organizationRepository.findByOrgNumber(orgNumber);
+ }
+
+ /**
+ * Finds an organization by its name.
+ * @param name the name of the organization
+ * @return the Organization if found, null otherwise
+ */
+ public Organization findByOrgName(String name) {
+ return organizationRepository.findByOrgName(name);
+ }
+}
diff --git a/src/main/java/edu/group5/app/model/user/Customer.java b/src/main/java/edu/group5/app/model/user/Customer.java
new file mode 100644
index 0000000..93f03ac
--- /dev/null
+++ b/src/main/java/edu/group5/app/model/user/Customer.java
@@ -0,0 +1,59 @@
+package edu.group5.app.model.user;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Customer class represents a customer in the system.
+ * It extends the User class and includes additional functionality specific to customers,
+ * such as managing their preferences for organizations.
+ * Each customer has a map of preferences that associates organization numbers with the corresponding Organization objects.
+ */
+public class Customer extends User {
+ private List preferences;
+/**
+ * Constructs a new Customer object for new registrations.
+ * The role is automatically set to "Customer" and the preferences list is initialized empty.
+ *
+ * @param userId the unique identifier for the user, must be positive
+ * @param firstName the first name of the customer
+ * @param lastName the last name of the customer
+ * @param email the email address of the customer
+ * @param passwordHash the hashed password of the customer
+ * @throws IllegalArgumentException if any parameter is invalid (null, empty, or userId ≤ 0)
+ */
+public Customer(int userId, String firstName, String lastName,
+ String email, String passwordHash) {
+ super(userId, firstName, lastName, email, passwordHash);
+ this.preferences = new ArrayList<>();
+}
+
+ public List getPreferences() {
+ return preferences;
+ }
+
+ @Override
+ public String getRole() {
+ return UserRepository.ROLE_CUSTOMER;
+ }
+
+ public void addPreference(int orgNumber) {
+ if (orgNumber <= 0) {
+ throw new IllegalArgumentException("Organization number must be a positive integer");
+ }
+ if (preferences.contains(orgNumber)) {
+ throw new IllegalArgumentException("Organization number already in preferences");
+ }
+ preferences.add(orgNumber);
+ }
+
+ public void removePreference(int orgNumber) {
+ if (orgNumber <= 0) {
+ throw new IllegalArgumentException("Organization number must be a positive integer");
+ }
+ if (!preferences.contains(orgNumber)) {
+ throw new IllegalArgumentException("Organization number not found in preferences");
+ }
+ preferences.remove(Integer.valueOf(orgNumber));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/group5/app/model/user/User.java b/src/main/java/edu/group5/app/model/user/User.java
index e163785..411538d 100644
--- a/src/main/java/edu/group5/app/model/user/User.java
+++ b/src/main/java/edu/group5/app/model/user/User.java
@@ -1,5 +1,117 @@
package edu.group5.app.model.user;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+/**
+ * User class represents a user in the system. It is an abstract class that will be extended by specific user types such as Donor, Recipient, and Admin.
+ * Each user has a unique userId, a role that defines their permissions in the system, and personal information such as first name, last name, email, and password hash.
+ * The constructor validates that all required fields are provided and throws an IllegalArgumentException if any of the fields are null or empty.
+ * This ensures that the User objects are always in a valid state when created.
+ * The class also includes a method to verify the user's password
+ * by comparing the provided plaintext password with the stored hashed password using BCrypt.
+ *
+ */
+public abstract class User {
+ private int userId;
+ private String firstName;
+ private String lastName;
+ private String email;
+ private String passwordHash;
+
+ /**
+ * Constructor for User class. Validates that all required fields
+ * are provided and throws an IllegalArgumentException if any of the fields are null or empty.
+ * @param userId the unique identifier for the user, must be a positive integer
+ * @param firstName the first name of the user
+ * @param lastName the last name of the user
+ * @param email the email address of the user
+ * @param passwordHash the hashed password of the user, used for authentication purposes
+ */
+ public User(int userId, String firstName,
+ String lastName, String email, String passwordHash) {
+ if (userId <= 0) {
+ throw new IllegalArgumentException("User ID must be positive");
+ }
+ if (firstName == null || firstName.trim().isEmpty()) {
+ throw new IllegalArgumentException("First name cannot be null or empty");
+ }
+ if (lastName == null || lastName.trim().isEmpty()) {
+ throw new IllegalArgumentException("Last name cannot be null or empty");
+ }
+ if (email == null || email.trim().isEmpty()) {
+ throw new IllegalArgumentException("Email cannot be null or empty");
+ }
+ if (passwordHash == null || passwordHash.trim().isEmpty()) {
+ throw new IllegalArgumentException("Password hash cannot be null or empty");
+ }
+ this.userId = userId;
+ this.firstName = firstName.trim();
+ this.lastName = lastName.trim();
+ this.email = email.trim();
+ this.passwordHash = passwordHash;
+}
+
+ /**
+ * Gets the unique identifier for the user.
+ * @return the userId of the user
+ */
+ public int getUserId() {
+ return userId;
+ }
+
+ /**
+ * Gets the role of the user (e.g., "Customer", "Admin").
+ * @return the role of the user
+ */
+ public abstract String getRole();
-public class User {
+ /**
+ * Gets the first name of the user.
+ * @return the first name of the user
+ */
+ public String getFirstName() {
+ return firstName;
+ }
+
+ /**
+ * Gets the last name of the user.
+ * @return the last name of the user
+ */
+ public String getLastName() {
+ return lastName;
+ }
+
+ /**
+ * Gets the email address of the user.
+ * @return the email of the user
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * Gets the hashed password of the user.
+ * This is used for authentication purposes and should not be exposed in plaintext.
+ * @return the password hash of the user
+ */
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
+ /**
+ * Verifies if the provided password matches the stored password hash.
+ * This method uses BCrypt to compare the plaintext password with the hashed password.
+ * @param password the plaintext password to verify
+ * @return true if the password is correct, false otherwise
+ * @throws IllegalArgumentException if the password is null, empty, or longer than 72 characters (BCrypt limit)
+ */
+ public boolean verifyPassword(char[] password) {
+ if (password == null || password.length == 0) {
+ return false;
+ }
+ if (password.length > 72) { // BCrypt has a maximum password length of 72 bytes
+ throw new IllegalArgumentException("Password cannot be longer than 72 characters");
+ }
+ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+ return encoder.matches(new String(password), this.passwordHash);
+ }
}
diff --git a/src/main/java/edu/group5/app/model/user/UserRepository.java b/src/main/java/edu/group5/app/model/user/UserRepository.java
new file mode 100644
index 0000000..193f433
--- /dev/null
+++ b/src/main/java/edu/group5/app/model/user/UserRepository.java
@@ -0,0 +1,149 @@
+package edu.group5.app.model.user;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import edu.group5.app.model.DBRepository;
+
+public class UserRepository extends DBRepository {
+ public final static String ROLE_CUSTOMER = "Customer";
+
+ /**
+ * Constructs UserRepository using Hashmap,
+ * and extends the content from DBRepository.
+ *
+ * @param content the underlying map used to store users,
+ * where the key represents the user ID
+ */
+ public UserRepository(List