diff --git a/.github/workflows/java_tests.yml b/.github/workflows/java_tests.yml index 896fdc71..702a9cc0 100644 --- a/.github/workflows/java_tests.yml +++ b/.github/workflows/java_tests.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: # Add any future services relative paths from . here - service: [user] + service: [user, matching] steps: @@ -41,7 +41,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: junit-test-results + name: ${{ matrix.service }}-junit-test-results path: | server/${{ matrix.service }}/build/test-results server/${{ matrix.service }}/build/reports/tests diff --git a/deployment/debug/compose.yml b/deployment/debug/compose.yml new file mode 100644 index 00000000..ee3926b6 --- /dev/null +++ b/deployment/debug/compose.yml @@ -0,0 +1,89 @@ +# This file is used to deploy the microservices using Docker Compose. +# It defines the services, their images, build context, and ports. + +services: + gateway-service: + build: ../../server/gateway + container_name: meetatmensa-gateway + ports: + - "8080:80" + depends_on: + - matching-service + - user-service + - genai-service + networks: + - backend + + matching-service: + build: ../../server/matching + container_name: meetatmensa-matching + expose: + - "80" + ports: + - "8081:80" + depends_on: + - match-database + networks: + - backend + + user-service: + build: ../../server/user + container_name: meetatmensa-user + expose: + - "80" + ports: + - "8082:80" + depends_on: + - user-database + networks: + - backend + + genai-service: + build: ../../server/genai + container_name: meetatmensa-genai + expose: + - "80" + ports: + - "8083:80" + networks: + - backend + + client-service: + build: ../../client + container_name: meetatmensa-client + ports: + - "80:80" + + match-database: + build: ../../server/database/matchdb + container_name: meetatmensa-matchdb + # TODO: Implement password passed via secret + environment: + - MYSQL_ROOT_PASSWORD=root + volumes: + - matchdb_data:/var/lib/mysql + expose: + - "3306" + networks: + - backend + + user-database: + build: ../../server/database/userdb + container_name: meetatmensa-userdb + # TODO: Implement password passed via secret + environment: + - MYSQL_ROOT_PASSWORD=root + volumes: + - userdb_data:/var/lib/mysql + expose: + - "3306" + networks: + - backend + +networks: + backend: + driver: bridge + +volumes: + matchdb_data: + userdb_data: \ No newline at end of file diff --git a/server/matching/build.gradle b/server/matching/build.gradle index 5cef5559..a7565ba5 100644 --- a/server/matching/build.gradle +++ b/server/matching/build.gradle @@ -23,6 +23,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.testcontainers:testcontainers:1.21.3' + testImplementation 'org.testcontainers:mysql:1.21.3' + testImplementation "org.testcontainers:junit-jupiter:1.21.3" testRuntimeOnly 'org.junit.platform:junit-platform-launcher' runtimeOnly 'com.mysql:mysql-connector-j:9.3.0' } diff --git a/server/matching/src/main/java/meet_at_mensa/matching/client/UserClient.java b/server/matching/src/main/java/meet_at_mensa/matching/client/UserClient.java index 237eee7c..6360bc3e 100644 --- a/server/matching/src/main/java/meet_at_mensa/matching/client/UserClient.java +++ b/server/matching/src/main/java/meet_at_mensa/matching/client/UserClient.java @@ -10,9 +10,11 @@ import org.openapitools.client.model.User; import org.openapitools.model.UserCollection; +import org.springframework.stereotype.Service; import meet_at_mensa.matching.exception.RestException; +@Service public class UserClient { private ApiClient defaultClient; diff --git a/server/matching/src/main/java/meet_at_mensa/matching/service/GroupService.java b/server/matching/src/main/java/meet_at_mensa/matching/service/GroupService.java index b3f78de9..ccc0e7c2 100644 --- a/server/matching/src/main/java/meet_at_mensa/matching/service/GroupService.java +++ b/server/matching/src/main/java/meet_at_mensa/matching/service/GroupService.java @@ -1,16 +1,22 @@ package meet_at_mensa.matching.service; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import org.openapitools.model.Group; import org.openapitools.model.Location; +import org.openapitools.model.MatchStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import meet_at_mensa.matching.exception.GroupNotFoundException; +import meet_at_mensa.matching.exception.MatchNotFoundException; import meet_at_mensa.matching.model.GroupEntity; +import meet_at_mensa.matching.model.MatchEntity; import meet_at_mensa.matching.repository.GroupRepository; +import meet_at_mensa.matching.repository.MatchRepository; @Service public class GroupService { @@ -20,10 +26,10 @@ public class GroupService { private GroupRepository groupRepository; @Autowired - private ConversationStarterService conversationStarterService; + private MatchRepository matchRepository; @Autowired - private MatchService matchService; + private ConversationStarterService conversationStarterService; /** * Searches the database for a group with this groupID @@ -43,7 +49,7 @@ public Group getGroup(UUID groupID) { groupEntity.getDate(), // date groupEntity.getTimeslot(), // timeslot groupEntity.getLocation(), // Location (enum) - matchService.getMatchStatus(groupID), // List + getGroupStatus(groupID), // List conversationStarterService.getPrompts(groupID) // ConversationStarterCollection ); @@ -74,4 +80,40 @@ protected UUID registerGroup(LocalDate date, Integer timeslot, Location location return group.getGroupID(); } + + /** + * Retieves the match status for all users in a group + * + * + * @param groupID UUID of the group + * @return List objects representing each users' status + * @throws MatchNotFoundException if no user with id {userID} is found + */ + private List getGroupStatus(UUID groupID){ + + // get all matches in the system for a given group ID + Iterable matchEntities = matchRepository.findByGroupID(groupID); + + // if list is empty, throw exception + if (!matchEntities.iterator().hasNext()) { + throw new MatchNotFoundException(); + } + + // create blank matchstatus list + List groupStatus = new ArrayList(); + + for (MatchEntity matchEntity : matchEntities) { + + // create a match Status object for a single user + MatchStatus matchStatus = new MatchStatus(matchEntity.getUserID(), matchEntity.getInviteStatus()); + + // add it to the list + groupStatus.add(matchStatus); + + } + + // return the list + return groupStatus; + + } } diff --git a/server/matching/src/main/java/meet_at_mensa/matching/service/MatchService.java b/server/matching/src/main/java/meet_at_mensa/matching/service/MatchService.java index 22118147..bbc9bae9 100644 --- a/server/matching/src/main/java/meet_at_mensa/matching/service/MatchService.java +++ b/server/matching/src/main/java/meet_at_mensa/matching/service/MatchService.java @@ -1,13 +1,10 @@ package meet_at_mensa.matching.service; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; import org.openapitools.model.InviteStatus; import org.openapitools.model.Match; import org.openapitools.model.MatchCollection; -import org.openapitools.model.MatchStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -27,42 +24,6 @@ public class MatchService { @Autowired private GroupService groupService; - - /** - * Retieves the match status for all users in a group - * - * - * @param groupID UUID of the group - * @return List objects representing each users' status - * @throws MatchNotFoundException if no user with id {userID} is found - */ - public List getMatchStatus(UUID groupID) { - - // get all matches in the system for a given group ID - Iterable matchEntities = matchRepository.findByGroupID(groupID); - - // if list is empty, throw exception - if (!matchEntities.iterator().hasNext()) { - throw new MatchNotFoundException(); - } - - // create blank matchstatus list - List groupStatus = new ArrayList(); - - for (MatchEntity matchEntity : matchEntities) { - - // create a match Status object for a single user - MatchStatus matchStatus = new MatchStatus(matchEntity.getUserID(), matchEntity.getInviteStatus()); - - // add it to the list - groupStatus.add(matchStatus); - - } - - // return the list - return groupStatus; - } - /** * Retieves a specific match by it's matchID * diff --git a/server/matching/src/test/java/meet_at_mensa/matching/MatchingApplicationTests.java b/server/matching/src/test/java/meet_at_mensa/matching/MatchingApplicationTests.java index 50f10689..5dc24de3 100644 --- a/server/matching/src/test/java/meet_at_mensa/matching/MatchingApplicationTests.java +++ b/server/matching/src/test/java/meet_at_mensa/matching/MatchingApplicationTests.java @@ -2,11 +2,31 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest -// MatchingApplicationTests is a test class for the matching application +@Testcontainers +// MatchingpplicationTests is a test class for the matching application class MatchingApplicationTests { + @Container + static MySQLContainer matchdb = new MySQLContainer<>("mysql:8.0") + .withDatabaseName("matchdb") + .withInitScript("init_matchdb_test.sql") + .withUsername("root") + .withPassword("root"); + + @DynamicPropertySource + static void overrideProps(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", matchdb::getJdbcUrl); + registry.add("spring.datasource.username", matchdb::getUsername); + registry.add("spring.datasource.password", matchdb::getPassword); + } + @Test void contextLoads() { } diff --git a/server/matching/src/test/resources/init_matchdb_test.sql b/server/matching/src/test/resources/init_matchdb_test.sql new file mode 100644 index 00000000..6ca1e92e --- /dev/null +++ b/server/matching/src/test/resources/init_matchdb_test.sql @@ -0,0 +1,67 @@ +-- matches table +-- which user has been matched with which group + -- match_id: UUID of the match, primary key + -- user_id: UUID of the matched user + -- group_id: UUID of group the user has been matched to + -- invite_status: Has the user RSVP'd? +CREATE TABLE IF NOT EXISTS `matches` ( + match_id VARBINARY(16) PRIMARY KEY, + user_id VARBINARY(16) NOT NULL, + group_id VARBINARY(16) NOT NULL, + invite_status VARCHAR(255) NOT NULL +); + +-- groups table +-- information about groups + -- group_id: UUID of the group + -- meet_date: date a group is set to meet + -- meet_timeslot: time a group is set to meet, encoded as Integer + -- meet_location: mensa a group is set to meet at +CREATE TABLE IF NOT EXISTS `groups` ( + group_id VARBINARY(16) PRIMARY KEY, + meet_date DATE NOT NULL, + meet_timeslot INT NOT NULL, + meet_location VARCHAR(255) NOT NULL +); + +-- prompts table +-- contains conversation starters + -- prompt_id: UUID of the prompt + -- group_id: UUID of the group this conversation starter prompt belongs to + -- prompt: prompt itself +CREATE TABLE IF NOT EXISTS `prompts` ( + prompt_id VARBINARY(16) PRIMARY KEY, + group_id VARBINARY(16) NOT NULL, + prompt VARCHAR(1023) NOT NULL +); + +-- match requests table +-- individual match requests + -- request_id: UUID of the request, primary key + -- user_id: UUID of user making the request + -- group_id: UUID of the group the user was matched to. Null if Unmatched + -- request_date: date the user would like to be matched + -- degree_pref: does user prefer matches with same degree? + -- age_pref: does user prefer matches with same age? + -- gender_pref: does user prefer matches with same gender? +CREATE TABLE IF NOT EXISTS `match_requests` ( + request_id VARBINARY(16) PRIMARY KEY, + user_id VARBINARY(16) NOT NULL, + request_date DATE NOT NULL, + request_location VARCHAR(255) NOT NULL, + degree_pref BIT NOT NULL, + age_pref BIT NOT NULL, + gender_pref BIT NOT NULL, + request_status VARCHAR(255) NOT NULL +); + +-- match timeslot table +-- What time-slots has a user selected in their requests + -- timeslot_id: UUID primary key (Unique) + -- request_id: UUID of the corresponding request + -- time_slot time slot (encoded as integer) +CREATE TABLE IF NOT EXISTS `match_timeslots` ( + timeslot_id VARBINARY(16) PRIMARY KEY, + request_id VARBINARY(16) NOT NULL, + request_timeslot INT NOT NULL +); \ No newline at end of file