From 6bb10fdce6ea3043b1258776c7f37d99f7cf85e2 Mon Sep 17 00:00:00 2001
From: ddingmin
Date: Tue, 13 Feb 2024 00:55:31 +0900
Subject: [PATCH 01/11] =?UTF-8?q?fix:=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4?=
=?UTF-8?q?=EA=B2=B0=20(#210)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/net/teumteum/meeting/service/MeetingService.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/main/java/net/teumteum/meeting/service/MeetingService.java b/src/main/java/net/teumteum/meeting/service/MeetingService.java
index 2657f43..99cfc41 100644
--- a/src/main/java/net/teumteum/meeting/service/MeetingService.java
+++ b/src/main/java/net/teumteum/meeting/service/MeetingService.java
@@ -112,7 +112,6 @@ public PageDto getMeetingsBySpecification(Pageable pageable, T
} else if (Boolean.TRUE.equals(isBookmarked)) {
spec = MeetingSpecification.withBookmarkedUserId(userId);
}
-
var meetings = meetingRepository.findAll(spec, pageable);
return PageDto.of(MeetingsResponse.of(meetings.getContent()), meetings.hasNext());
From 9eac8c543cc919e3ec3dca4e8726d75d97607440 Mon Sep 17 00:00:00 2001
From: ChoiDongKuen
Date: Tue, 13 Feb 2024 02:16:48 +0900
Subject: [PATCH 02/11] =?UTF-8?q?fix:=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?=
=?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=97=90=EB=9F=AC=20=EA=B8=B4=EA=B8=89=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95=20(#214)=20(#215)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/net/teumteum/alert/app/AlertHandler.java | 4 ++--
.../core/security/filter/JwtAuthenticationFilter.java | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/net/teumteum/alert/app/AlertHandler.java b/src/main/java/net/teumteum/alert/app/AlertHandler.java
index 86e6bc6..56b6400 100644
--- a/src/main/java/net/teumteum/alert/app/AlertHandler.java
+++ b/src/main/java/net/teumteum/alert/app/AlertHandler.java
@@ -45,7 +45,7 @@ public void handleBeforeMeetingAlerts(BeforeMeetingAlerted alerted) {
@Async(ALERT_EXECUTOR)
@EventListener(EndMeetingAlerted.class)
- public void handleEndMeetingAlerts(EndMeetingAlerted alerted) {
+ public void handleStartMeetingAlerts(EndMeetingAlerted alerted) {
userAlertService.findAllByUserId(alerted.userIds())
.stream()
.map(userAlert -> Pair.of(userAlert.getToken(),
@@ -64,7 +64,7 @@ private String toCommaString(List ids) {
for (int i = 0; i < ids.size() - 1; i++) {
stringBuilder.append(ids.get(i)).append(",");
}
- stringBuilder.append(ids.get(ids.size() - 1));
+ stringBuilder.append(ids.getLast());
return stringBuilder.toString();
}
diff --git a/src/main/java/net/teumteum/core/security/filter/JwtAuthenticationFilter.java b/src/main/java/net/teumteum/core/security/filter/JwtAuthenticationFilter.java
index 20ef8e3..36e960d 100644
--- a/src/main/java/net/teumteum/core/security/filter/JwtAuthenticationFilter.java
+++ b/src/main/java/net/teumteum/core/security/filter/JwtAuthenticationFilter.java
@@ -72,7 +72,7 @@ private void saveUserAuthentication(User user) {
private String resolveTokenFromRequest(HttpServletRequest request) {
String token = request.getHeader(jwtProperty.getAccess().getHeader());
- if (token.toLowerCase().startsWith(jwtProperty.getBearer().toLowerCase())) {
+ if (StringUtils.hasText(token) && token.toLowerCase().startsWith(jwtProperty.getBearer().toLowerCase())) {
return token.substring(7);
}
return null;
From 1a7052c14d5cec13ed0fd0ccba918f15e707f84b Mon Sep 17 00:00:00 2001
From: ChoiDongKuen
Date: Wed, 14 Feb 2024 04:22:36 +0900
Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EA=B8=B0=EC=A1=B4=20=ED=9A=8C?=
=?UTF-8?q?=EC=9B=90=20=EB=A6=AC=EB=B7=B0=20=EC=A1=B0=ED=9A=8C=20API=20?=
=?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20(#218)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: userId 에 해당하는 유저 리뷰 조회 API 로직 변경 (#217)
* test: 통합테스트 수정 (#217)
* test: 단위테스트 수정 (#217)
---
.../user/controller/UserController.java | 6 +++---
.../teumteum/user/domain/UserRepository.java | 9 ++++----
.../domain/response/UserReviewResponse.java | 10 +++++++++
.../domain/response/UserReviewsResponse.java | 8 ++++---
.../teumteum/user/service/UserService.java | 6 ++++--
.../java/net/teumteum/integration/Api.java | 4 ++--
.../integration/UserIntegrationTest.java | 4 +++-
.../user/controller/UserControllerTest.java | 19 ++++++++---------
.../unit/user/service/UserServiceTest.java | 21 ++++++++++---------
.../user/domain/UserRepositoryTest.java | 7 ++++---
10 files changed, 56 insertions(+), 38 deletions(-)
create mode 100644 src/main/java/net/teumteum/user/domain/response/UserReviewResponse.java
diff --git a/src/main/java/net/teumteum/user/controller/UserController.java b/src/main/java/net/teumteum/user/controller/UserController.java
index 6523683..feb37d9 100644
--- a/src/main/java/net/teumteum/user/controller/UserController.java
+++ b/src/main/java/net/teumteum/user/controller/UserController.java
@@ -116,10 +116,10 @@ public void registerReview(
userService.registerReview(meetingId, getCurrentUserId(), request);
}
- @GetMapping("/reviews")
+ @GetMapping("/{userId}/reviews")
@ResponseStatus(HttpStatus.OK)
- public List getUserReviews() {
- return userService.getUserReviews(getCurrentUserId());
+ public UserReviewsResponse getUserReviews(@PathVariable("userId") Long userId) {
+ return userService.getUserReviews(userId);
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
diff --git a/src/main/java/net/teumteum/user/domain/UserRepository.java b/src/main/java/net/teumteum/user/domain/UserRepository.java
index d0380f2..b611a17 100644
--- a/src/main/java/net/teumteum/user/domain/UserRepository.java
+++ b/src/main/java/net/teumteum/user/domain/UserRepository.java
@@ -3,7 +3,7 @@
import java.util.List;
import java.util.Optional;
import net.teumteum.core.security.Authenticated;
-import net.teumteum.user.domain.response.UserReviewsResponse;
+import net.teumteum.user.domain.response.UserReviewResponse;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -15,7 +15,8 @@ public interface UserRepository extends JpaRepository {
Optional findByAuthenticatedAndOAuthId(@Param("authenticated") Authenticated authenticated,
@Param("oAuthId") String oAuthId);
- @Query("select new net.teumteum.user.domain.response.UserReviewsResponse(r,count(r)) "
- + "from users u join u.reviews r where u.id = :userId group by r")
- List countUserReviewsByUserId(@Param("userId") Long userId);
+ @Query("select new net.teumteum.user.domain.response.UserReviewResponse(r, count(r)) "
+ + "from users u join u.reviews r where u = :user group by r")
+ List countUserReviewsByUser(@Param("user") User user);
+
}
diff --git a/src/main/java/net/teumteum/user/domain/response/UserReviewResponse.java b/src/main/java/net/teumteum/user/domain/response/UserReviewResponse.java
new file mode 100644
index 0000000..8ec04a8
--- /dev/null
+++ b/src/main/java/net/teumteum/user/domain/response/UserReviewResponse.java
@@ -0,0 +1,10 @@
+package net.teumteum.user.domain.response;
+
+import net.teumteum.user.domain.Review;
+
+public record UserReviewResponse(
+ Review review,
+ long count
+) {
+
+}
diff --git a/src/main/java/net/teumteum/user/domain/response/UserReviewsResponse.java b/src/main/java/net/teumteum/user/domain/response/UserReviewsResponse.java
index 7454f52..bf53ed6 100644
--- a/src/main/java/net/teumteum/user/domain/response/UserReviewsResponse.java
+++ b/src/main/java/net/teumteum/user/domain/response/UserReviewsResponse.java
@@ -1,10 +1,12 @@
package net.teumteum.user.domain.response;
-import net.teumteum.user.domain.Review;
+import java.util.List;
public record UserReviewsResponse(
- Review review,
- long count
+ List reviews
) {
+ public static UserReviewsResponse of(List reviews) {
+ return new UserReviewsResponse(reviews);
+ }
}
diff --git a/src/main/java/net/teumteum/user/service/UserService.java b/src/main/java/net/teumteum/user/service/UserService.java
index 8429d05..723d628 100644
--- a/src/main/java/net/teumteum/user/service/UserService.java
+++ b/src/main/java/net/teumteum/user/service/UserService.java
@@ -116,8 +116,10 @@ public void registerReview(Long meetingId, Long currentUserId, ReviewRegisterReq
});
}
- public List getUserReviews(Long userId) {
- return userRepository.countUserReviewsByUserId(userId);
+ public UserReviewsResponse getUserReviews(Long userId) {
+ var user = getUser(userId);
+
+ return UserReviewsResponse.of(userRepository.countUserReviewsByUser(user));
}
public FriendsResponse findFriendsByUserId(Long userId) {
diff --git a/src/test/java/net/teumteum/integration/Api.java b/src/test/java/net/teumteum/integration/Api.java
index a65e350..0002b52 100644
--- a/src/test/java/net/teumteum/integration/Api.java
+++ b/src/test/java/net/teumteum/integration/Api.java
@@ -196,10 +196,10 @@ ResponseSpec deleteMeeting(String accessToken, Long meetingId) {
.exchange();
}
- ResponseSpec getUserReviews(String accessToken) {
+ ResponseSpec getUserReviews(Long userId, String accessToken) {
return webTestClient
.get()
- .uri("/users/reviews")
+ .uri("/users/" + userId + "/reviews")
.header(HttpHeaders.AUTHORIZATION, accessToken)
.exchange();
}
diff --git a/src/test/java/net/teumteum/integration/UserIntegrationTest.java b/src/test/java/net/teumteum/integration/UserIntegrationTest.java
index 916bb80..bb42461 100644
--- a/src/test/java/net/teumteum/integration/UserIntegrationTest.java
+++ b/src/test/java/net/teumteum/integration/UserIntegrationTest.java
@@ -346,10 +346,12 @@ void Get_user_reviews() {
// given
var existUser = repository.saveAndGetUser();
+ var userId = existUser.getId();
+
securityContextSetting.set(existUser.getId());
// when
- var expected = api.getUserReviews(VALID_TOKEN);
+ var expected = api.getUserReviews(userId, VALID_TOKEN);
// then
Assertions.assertThat(expected.expectStatus().isOk()
diff --git a/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java b/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
index f425e05..77afd49 100644
--- a/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
+++ b/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
@@ -37,6 +37,7 @@
import net.teumteum.user.domain.request.UserRegisterRequest;
import net.teumteum.user.domain.request.UserWithdrawRequest;
import net.teumteum.user.domain.response.UserRegisterResponse;
+import net.teumteum.user.domain.response.UserReviewResponse;
import net.teumteum.user.domain.response.UserReviewsResponse;
import net.teumteum.user.service.UserService;
import org.junit.jupiter.api.BeforeEach;
@@ -238,27 +239,25 @@ void Register_reviews_with_400_bad_request() throws Exception {
class Get_user_reviews_api_unit {
@Test
- @DisplayName("로그인한 회원 id 에 해당하는 회원 리뷰와 200 OK을 반환한다.")
+ @DisplayName("user id 에 해당하는 회원 리뷰와 200 OK을 반환한다.")
void Get_user_reviews_with_200_ok() throws Exception {
// given
var userId = 1L;
- given(securityService.getCurrentUserId()).willReturn(userId);
-
given(userService.getUserReviews(anyLong()))
- .willReturn(List.of(new UserReviewsResponse(별로에요, 2L),
- new UserReviewsResponse(최고에요, 3L)));
+ .willReturn(UserReviewsResponse.of(List.of(new UserReviewResponse(별로에요, 2L),
+ new UserReviewResponse(최고에요, 3L))));
// when & then
- mockMvc.perform(get("/users/reviews")
+ mockMvc.perform(get("/users/{userId}/reviews", 1)
.with(csrf())
.header(AUTHORIZATION, VALID_ACCESS_TOKEN))
.andDo(print())
.andExpect(status().isOk())
- .andExpect(jsonPath("$", notNullValue()))
- .andExpect(jsonPath("$", hasSize(2)))
- .andExpect(jsonPath("$[0].count", is(2)))
- .andExpect(jsonPath("$[0].review").value("별로에요"));
+ .andExpect(jsonPath("$.reviews", notNullValue()))
+ .andExpect(jsonPath("$.reviews", hasSize(2)))
+ .andExpect(jsonPath("$.reviews[0].count", is(2)))
+ .andExpect(jsonPath("$.reviews[0].review").value("별로에요"));
}
}
diff --git a/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java b/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
index f682353..66b1668 100644
--- a/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
+++ b/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
@@ -30,7 +30,7 @@
import net.teumteum.user.domain.request.UserRegisterRequest;
import net.teumteum.user.domain.request.UserWithdrawRequest;
import net.teumteum.user.domain.response.UserRegisterResponse;
-import net.teumteum.user.domain.response.UserReviewsResponse;
+import net.teumteum.user.domain.response.UserReviewResponse;
import net.teumteum.user.service.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
@@ -251,23 +251,24 @@ class Get_user_reviews_api_unit {
void Return_user_reviews_with_200_ok() {
// given
var userId = 1L;
+ var existUser = UserFixture.getIdUser();
- var response = List.of(new UserReviewsResponse(최고에요, 2L)
- , new UserReviewsResponse(별로에요, 3L));
+ var response = List.of(new UserReviewResponse(최고에요, 2L)
+ , new UserReviewResponse(별로에요, 3L));
- given(userRepository.countUserReviewsByUserId(anyLong())).willReturn(response);
+ given(userRepository.findById(anyLong())).willReturn(Optional.of(existUser));
+ given(userRepository.countUserReviewsByUser(any(User.class))).willReturn(response);
// when
var result = userService.getUserReviews(userId);
// then
- assertThat(result).hasSize(2);
- assertThat(result.get(0).review()).isEqualTo(최고에요);
- assertThat(result.get(0).count()).isEqualTo(2L);
- assertThat(result.get(1).review()).isEqualTo(별로에요);
- assertThat(result.get(1).count()).isEqualTo(3L);
+ assertThat(result.reviews()).hasSize(2);
+ assertThat(result.reviews().get(0).review()).isEqualTo(최고에요);
+ assertThat(result.reviews().get(0).count()).isEqualTo(2L);
+ assertThat(result.reviews().get(1).review()).isEqualTo(별로에요);
- verify(userRepository, times(1)).countUserReviewsByUserId(anyLong());
+ verify(userRepository, times(1)).countUserReviewsByUser(any(User.class));
}
}
}
diff --git a/src/test/java/net/teumteum/user/domain/UserRepositoryTest.java b/src/test/java/net/teumteum/user/domain/UserRepositoryTest.java
index 59d607b..a046f11 100644
--- a/src/test/java/net/teumteum/user/domain/UserRepositoryTest.java
+++ b/src/test/java/net/teumteum/user/domain/UserRepositoryTest.java
@@ -7,7 +7,7 @@
import jakarta.persistence.EntityManager;
import java.util.Optional;
import net.teumteum.core.config.AppConfig;
-import net.teumteum.user.domain.response.UserReviewsResponse;
+import net.teumteum.user.domain.response.UserReviewResponse;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@@ -85,13 +85,14 @@ void Count_user_reviews_by_user_id() {
entityManager.clear();
// when
- var result = userRepository.countUserReviewsByUserId(id);
+ var result = userRepository.countUserReviewsByUser(existUser);
// then
Assertions.assertThat(result)
.isNotEmpty()
.hasSize(3)
- .extracting(UserReviewsResponse::review, UserReviewsResponse::count)
+ .extracting(UserReviewResponse::review,
+ UserReviewResponse::count)
.contains(
Assertions.tuple(최고에요, 3L),
Assertions.tuple(별로에요, 1L),
From d7f3a3e44c890ba7731e0d455c12e45646407ca8 Mon Sep 17 00:00:00 2001
From: ddingmin
Date: Wed, 14 Feb 2024 22:58:46 +0900
Subject: [PATCH 04/11] =?UTF-8?q?docs:=20readme=20=EC=9E=91=EC=84=B1=20(#2?=
=?UTF-8?q?22)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 39 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index af97483..7fd4c45 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,38 @@
-# tmtm-server
\ No newline at end of file
+
+
+# Teum-Teum
+
+> 사람 사이의 **틈**을 이어주는 IT 커리어 네트워킹 서비스 `repo:server`
+
+[![download](https://img.shields.io/badge/playstore-download-brightgreen?style=for-the-badge&logo=google&logoColor=white&color=36B2FF)](https://play.google.com/store/apps/details?id=com.teumteum.teumteum&pcampaignid=web_share) ![Build](https://img.shields.io/github/actions/workflow/status/depromeet/teum-teum-server/integration-tester.yml?branch=develop&style=for-the-badge&logo=github&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/test_success_density/depromeet_teum-teum-server?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/quality_gate/depromeet_teum-teum-server/develop?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/github/v/release/depromeet/teum-teum-server?include_prereleases&style=for-the-badge&color=36B2FF)
+
+## 👋 Introduction
+
+![Behance](https://img.shields.io/badge/Behance-1769ff?style=for-the-badge&logo=behance&logoColor=white)
+
+## 🌐 Architecture
+
+## ⚒️ Tech Stack
+
+ ![SpringBoot](https://img.shields.io/badge/springboot-%236DB33F.svg?style=for-the-badge&logo=springboot&logoColor=white)![spring_data_JPA](https://img.shields.io/badge/spring_data_JPA-%236DB33F?style=for-the-badge&logo=databricks&logoColor=white)![SpringSecurity](https://img.shields.io/badge/spring_security-%236DB33F.svg?style=for-the-badge&logo=springsecurity&logoColor=white)![Gradle](https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white)
+
+ ![junit5](https://img.shields.io/badge/junit5-25A162?style=for-the-badge&logo=junit5&logoColor=white)![test_containers](https://img.shields.io/badge/test_containers-328ba3?style=for-the-badge&logo=reasonstudios&logoColor=white)
+
+ ![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white)![Redis](https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white)![Firebase](https://img.shields.io/badge/Firebase-039BE5?style=for-the-badge&logo=Firebase&logoColor=white)![flyway](https://img.shields.io/badge/flyway-CC0200?style=for-the-badge&logo=flyway&logoColor=white)
+
+ ![Amazon Ec2](https://img.shields.io/badge/amazon_ec2-FF9900.svg?style=for-the-badge&logo=amazonec2&logoColor=white)![Amazon S3](https://img.shields.io/badge/AWS_S3-569A31.svg?style=for-the-badge&logo=amazons3&logoColor=white)![Amazon RDS](https://img.shields.io/badge/amazon_RDS-527FFF.svg?style=for-the-badge&logo=amazonrds&logoColor=white)![Amazon ElastiCache](https://img.shields.io/badge/amazon_elasticache-FF9900.svg?style=for-the-badge&logo=amazondocumentdb&logoColor=white)![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&logo=nginx&logoColor=white)
+
+ ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)![GitHub Actions](https://img.shields.io/badge/github%20actions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white)![github container](https://img.shields.io/badge/github_container-181717.svg?style=for-the-badge&logo=github&logoColor=white)![squarespace](https://img.shields.io/badge/squarespace-000000?style=for-the-badge&logo=squarespace&logoColor=white)
+
+ ![SonarCloud](https://img.shields.io/badge/SonarCloud-F3702A?style=for-the-badge&logo=SonarCloud&logoColor=white)
+
+ ![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
+
+ ![sentry](https://img.shields.io/badge/sentry-362D59?style=for-the-badge&logo=sentry&logoColor=white)
+
+## 👥 Members
+
+| Server | Server | Server |
+|:----------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------:|
+| | | |
+| [choidongkuen](https://github.com/choidongkuen) | [xb205](https://github.com/devxb) | [ddingmin](https://github.com/ddingmin) |
From f5603e0cd6c42bf2f038f46308b982b7add87009 Mon Sep 17 00:00:00 2001
From: ddingmin
Date: Wed, 14 Feb 2024 22:59:33 +0900
Subject: [PATCH 05/11] =?UTF-8?q?docs:=20readme=20=EC=9E=91=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 7fd4c45..d2991fb 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,31 @@
[![download](https://img.shields.io/badge/playstore-download-brightgreen?style=for-the-badge&logo=google&logoColor=white&color=36B2FF)](https://play.google.com/store/apps/details?id=com.teumteum.teumteum&pcampaignid=web_share) ![Build](https://img.shields.io/github/actions/workflow/status/depromeet/teum-teum-server/integration-tester.yml?branch=develop&style=for-the-badge&logo=github&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/test_success_density/depromeet_teum-teum-server?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/quality_gate/depromeet_teum-teum-server/develop?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/github/v/release/depromeet/teum-teum-server?include_prereleases&style=for-the-badge&color=36B2FF)
-## 👋 Introduction
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![Behance](https://img.shields.io/badge/Behance-1769ff?style=for-the-badge&logo=behance&logoColor=white)
## 🌐 Architecture
+
+
+
## ⚒️ Tech Stack
@@ -30,7 +50,7 @@
![sentry](https://img.shields.io/badge/sentry-362D59?style=for-the-badge&logo=sentry&logoColor=white)
-## 👥 Members
+## 👥 Contributors
| Server | Server | Server |
|:----------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------:|
From e3552eb54a2b978b76f63b536e177ab253785217 Mon Sep 17 00:00:00 2001
From: ddingmin
Date: Wed, 14 Feb 2024 23:49:18 +0900
Subject: [PATCH 06/11] =?UTF-8?q?docs:=20readme=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index d2991fb..31a0898 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,9 @@
> 사람 사이의 **틈**을 이어주는 IT 커리어 네트워킹 서비스 `repo:server`
-[![download](https://img.shields.io/badge/playstore-download-brightgreen?style=for-the-badge&logo=google&logoColor=white&color=36B2FF)](https://play.google.com/store/apps/details?id=com.teumteum.teumteum&pcampaignid=web_share) ![Build](https://img.shields.io/github/actions/workflow/status/depromeet/teum-teum-server/integration-tester.yml?branch=develop&style=for-the-badge&logo=github&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/test_success_density/depromeet_teum-teum-server?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/quality_gate/depromeet_teum-teum-server/develop?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/github/v/release/depromeet/teum-teum-server?include_prereleases&style=for-the-badge&color=36B2FF)
+![Build](https://img.shields.io/github/actions/workflow/status/depromeet/teum-teum-server/integration-tester.yml?branch=develop&style=for-the-badge&logo=github&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/test_success_density/depromeet_teum-teum-server?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/sonar/quality_gate/depromeet_teum-teum-server/develop?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonar&logoColor=white&color=36B2FF) ![](https://img.shields.io/github/v/release/depromeet/teum-teum-server?include_prereleases&style=for-the-badge&color=36B2FF)
+
+[![download](https://img.shields.io/badge/playstore-download-brightgreen?style=social&logo=googleplay&color=36B2FF)](https://play.google.com/store/apps/details?id=com.teumteum.teumteum&pcampaignid=web_share) [![instagram](https://img.shields.io/badge/instagram-click-brightgreen?style=social&logo=instagram&color=36B2FF)](https://www.instagram.com/teumteum_official/) [![behance](https://img.shields.io/badge/behance-click-brightgreen?style=social&logo=behance&color=36B2FF)](https://www.behance.net/gallery/191510163/%08TEUMTEUM-IT-Career-Growth-Networking-Service)
---
@@ -25,30 +27,25 @@
-![Behance](https://img.shields.io/badge/Behance-1769ff?style=for-the-badge&logo=behance&logoColor=white)
-
## 🌐 Architecture
+
## ⚒️ Tech Stack
- ![SpringBoot](https://img.shields.io/badge/springboot-%236DB33F.svg?style=for-the-badge&logo=springboot&logoColor=white)![spring_data_JPA](https://img.shields.io/badge/spring_data_JPA-%236DB33F?style=for-the-badge&logo=databricks&logoColor=white)![SpringSecurity](https://img.shields.io/badge/spring_security-%236DB33F.svg?style=for-the-badge&logo=springsecurity&logoColor=white)![Gradle](https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white)
-
- ![junit5](https://img.shields.io/badge/junit5-25A162?style=for-the-badge&logo=junit5&logoColor=white)![test_containers](https://img.shields.io/badge/test_containers-328ba3?style=for-the-badge&logo=reasonstudios&logoColor=white)
-
- ![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white)![Redis](https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white)![Firebase](https://img.shields.io/badge/Firebase-039BE5?style=for-the-badge&logo=Firebase&logoColor=white)![flyway](https://img.shields.io/badge/flyway-CC0200?style=for-the-badge&logo=flyway&logoColor=white)
+![SpringBoot](https://img.shields.io/badge/springboot-%236DB33F.svg?style=for-the-badge&logo=springboot&logoColor=white)![spring_data_JPA](https://img.shields.io/badge/spring_data_JPA-%236DB33F?style=for-the-badge&logo=databricks&logoColor=white)![SpringSecurity](https://img.shields.io/badge/spring_security-%236DB33F.svg?style=for-the-badge&logo=springsecurity&logoColor=white) ![Gradle](https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white)
- ![Amazon Ec2](https://img.shields.io/badge/amazon_ec2-FF9900.svg?style=for-the-badge&logo=amazonec2&logoColor=white)![Amazon S3](https://img.shields.io/badge/AWS_S3-569A31.svg?style=for-the-badge&logo=amazons3&logoColor=white)![Amazon RDS](https://img.shields.io/badge/amazon_RDS-527FFF.svg?style=for-the-badge&logo=amazonrds&logoColor=white)![Amazon ElastiCache](https://img.shields.io/badge/amazon_elasticache-FF9900.svg?style=for-the-badge&logo=amazondocumentdb&logoColor=white)![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&logo=nginx&logoColor=white)
+![junit5](https://img.shields.io/badge/junit5-25A162?style=for-the-badge&logo=junit5&logoColor=white)![test_containers](https://img.shields.io/badge/test_containers-328ba3?style=for-the-badge&logo=reasonstudios&logoColor=white) ![gatling](https://img.shields.io/badge/gatling-FF9E2A?style=for-the-badge&logo=gatling&logoColor=white)
- ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)![GitHub Actions](https://img.shields.io/badge/github%20actions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white)![github container](https://img.shields.io/badge/github_container-181717.svg?style=for-the-badge&logo=github&logoColor=white)![squarespace](https://img.shields.io/badge/squarespace-000000?style=for-the-badge&logo=squarespace&logoColor=white)
+![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white)![Redis](https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white)![Firebase](https://img.shields.io/badge/Firebase-039BE5?style=for-the-badge&logo=Firebase&logoColor=white) ![flyway](https://img.shields.io/badge/flyway-CC0200?style=for-the-badge&logo=flyway&logoColor=white)
- ![SonarCloud](https://img.shields.io/badge/SonarCloud-F3702A?style=for-the-badge&logo=SonarCloud&logoColor=white)
+![Amazon Ec2](https://img.shields.io/badge/amazon_ec2-FF9900.svg?style=for-the-badge&logo=amazonec2&logoColor=white)![Amazon S3](https://img.shields.io/badge/AWS_S3-569A31.svg?style=for-the-badge&logo=amazons3&logoColor=white)![Amazon RDS](https://img.shields.io/badge/amazon_RDS-527FFF.svg?style=for-the-badge&logo=amazonrds&logoColor=white)![Amazon ElastiCache](https://img.shields.io/badge/amazon_elasticache-FF9900.svg?style=for-the-badge&logo=amazondocumentdb&logoColor=white)![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&logo=nginx&logoColor=white)
- ![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
+![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)![GitHub Actions](https://img.shields.io/badge/github%20actions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white)![github container](https://img.shields.io/badge/github_container-181717.svg?style=for-the-badge&logo=github&logoColor=white) ![squarespace](https://img.shields.io/badge/squarespace-000000?style=for-the-badge&logo=squarespace&logoColor=white)
- ![sentry](https://img.shields.io/badge/sentry-362D59?style=for-the-badge&logo=sentry&logoColor=white)
+![SonarCloud](https://img.shields.io/badge/SonarCloud-F3702A?style=for-the-badge&logo=SonarCloud&logoColor=white) ![sentry](https://img.shields.io/badge/sentry-362D59?style=for-the-badge&logo=sentry&logoColor=white) ![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
## 👥 Contributors
From 97ee24618c3c0d08e2355e46eba95ab537483bd4 Mon Sep 17 00:00:00 2001
From: devxb
Date: Thu, 15 Feb 2024 21:23:51 +0900
Subject: [PATCH 07/11] =?UTF-8?q?test:=20QA=EC=9A=A9=20EndMeetingAlerted?=
=?UTF-8?q?=EB=A5=BC=20=EB=B0=9C=ED=96=89=ED=95=9C=EB=8B=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../meeting/service/MeetingAlertPublisher.java | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java b/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
index 402c057..858650e 100644
--- a/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
+++ b/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
@@ -2,7 +2,6 @@
import java.time.LocalDateTime;
import java.time.ZoneId;
-import java.time.ZonedDateTime;
import lombok.RequiredArgsConstructor;
import net.teumteum.meeting.domain.BeforeMeetingAlerted;
import net.teumteum.meeting.domain.EndMeetingAlerted;
@@ -49,4 +48,21 @@ public void alertEndMeeting() {
new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
));
}
+
+ @Scheduled(cron = EVERY_ONE_MINUTES)
+ public void alertEndMeetingForQa() {
+ var today = LocalDateTime.now(ZoneId.of("Asia/Seoul"))
+ .withNano(0)
+ .withSecond(0)
+ .withMinute(0)
+ .withHour(0);
+
+ var future = today.plusDays(365);
+ var yesterday = today.minusDays(365);
+
+ var alertTargets = meetingRepository.findAlertMeetings(yesterday, future);
+ alertTargets.forEach(meeting -> eventPublisher.publishEvent(
+ new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
+ ));
+ }
}
From b71556dd3601418ad883ffac94f0a17d15d289d5 Mon Sep 17 00:00:00 2001
From: xb205 <62425964+devxb@users.noreply.github.com>
Date: Thu, 15 Feb 2024 23:02:12 +0900
Subject: [PATCH 08/11] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=EA=B0=80=20?=
=?UTF-8?q?=EC=82=AD=EC=A0=9C=EB=90=98=EC=97=88=EC=9D=84=EB=95=8C,=20?=
=?UTF-8?q?=EC=95=8C=EB=9E=8C=ED=86=A0=ED=81=B0=EB=8F=84=20=EC=82=AD?=
=?UTF-8?q?=EC=A0=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#2?=
=?UTF-8?q?26)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: 유저가 삭제되었을때, 알람토큰도 삭제하도록 수정한다
* test: ApplicationEventPublisher 바인딩 안되는 테스트 삭제
* refactor: UserDeletedEvent 핸들러 이름 변경
* test: 친구조회 테스트 수정
* refactor: code smell을 제거한다
---
.../net/teumteum/alert/app/AlertHandler.java | 6 +++++
.../alert/domain/UserAlertService.java | 8 +++++-
.../user/domain/UserDeletedEvent.java | 7 +++++
.../teumteum/user/service/UserService.java | 4 +++
.../integration/UserIntegrationTest.java | 26 -------------------
.../unit/user/service/UserServiceTest.java | 25 ++----------------
6 files changed, 26 insertions(+), 50 deletions(-)
create mode 100644 src/main/java/net/teumteum/user/domain/UserDeletedEvent.java
diff --git a/src/main/java/net/teumteum/alert/app/AlertHandler.java b/src/main/java/net/teumteum/alert/app/AlertHandler.java
index 56b6400..9cf1e80 100644
--- a/src/main/java/net/teumteum/alert/app/AlertHandler.java
+++ b/src/main/java/net/teumteum/alert/app/AlertHandler.java
@@ -14,6 +14,7 @@
import net.teumteum.meeting.domain.BeforeMeetingAlerted;
import net.teumteum.meeting.domain.EndMeetingAlerted;
import net.teumteum.user.UserRecommended;
+import net.teumteum.user.domain.UserDeletedEvent;
import org.springframework.context.annotation.Profile;
import org.springframework.context.event.EventListener;
import org.springframework.data.util.Pair;
@@ -29,6 +30,11 @@ public class AlertHandler {
private final AlertService alertService;
private final AlertPublisher alertPublisher;
+ @EventListener(UserDeletedEvent.class)
+ public void handleDeleteUserEvent(UserDeletedEvent userDeletedEvent) {
+ userAlertService.deleteAlertByUserId(userDeletedEvent.id());
+ }
+
@Async(ALERT_EXECUTOR)
@EventListener(BeforeMeetingAlerted.class)
public void handleBeforeMeetingAlerts(BeforeMeetingAlerted alerted) {
diff --git a/src/main/java/net/teumteum/alert/domain/UserAlertService.java b/src/main/java/net/teumteum/alert/domain/UserAlertService.java
index 4c6cb18..898754b 100644
--- a/src/main/java/net/teumteum/alert/domain/UserAlertService.java
+++ b/src/main/java/net/teumteum/alert/domain/UserAlertService.java
@@ -19,13 +19,19 @@ public class UserAlertService {
public void registerAlert(Long userId, RegisterAlertRequest registerAlertRequest) {
alertRepository.findByUserId(userId)
.ifPresentOrElse(userAlert -> {
- throw new IllegalArgumentException("이미 토큰이 생성된 user입니다. \"" + userId +"\"");
+ throw new IllegalArgumentException("이미 토큰이 생성된 user입니다. \"" + userId + "\"");
}, () -> {
var alert = new UserAlert(null, userId, registerAlertRequest.token());
alertRepository.save(alert);
});
}
+ @Transactional
+ public void deleteAlertByUserId(Long userId) {
+ alertRepository.findByUserId(userId).ifPresentOrElse(alertRepository::delete, () -> {
+ });
+ }
+
@Transactional
public void updateAlertToken(Long userId, UpdateAlertTokenRequest updateAlertTokenRequest) {
var userAlert = alertRepository.findByUserIdWithLock(userId)
diff --git a/src/main/java/net/teumteum/user/domain/UserDeletedEvent.java b/src/main/java/net/teumteum/user/domain/UserDeletedEvent.java
new file mode 100644
index 0000000..3cf8d56
--- /dev/null
+++ b/src/main/java/net/teumteum/user/domain/UserDeletedEvent.java
@@ -0,0 +1,7 @@
+package net.teumteum.user.domain;
+
+public record UserDeletedEvent(
+ Long id
+) {
+
+}
diff --git a/src/main/java/net/teumteum/user/service/UserService.java b/src/main/java/net/teumteum/user/service/UserService.java
index 073117b..76de43d 100644
--- a/src/main/java/net/teumteum/user/service/UserService.java
+++ b/src/main/java/net/teumteum/user/service/UserService.java
@@ -10,6 +10,7 @@
import net.teumteum.user.domain.BalanceGameType;
import net.teumteum.user.domain.InterestQuestion;
import net.teumteum.user.domain.User;
+import net.teumteum.user.domain.UserDeletedEvent;
import net.teumteum.user.domain.UserRepository;
import net.teumteum.user.domain.WithdrawReasonRepository;
import net.teumteum.user.domain.request.ReviewRegisterRequest;
@@ -23,6 +24,7 @@
import net.teumteum.user.domain.response.UserRegisterResponse;
import net.teumteum.user.domain.response.UserReviewsResponse;
import net.teumteum.user.domain.response.UsersGetByIdResponse;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
@@ -38,6 +40,7 @@ public class UserService {
private final RedisService redisService;
private final JwtService jwtService;
private final MeetingConnector meetingConnector;
+ private final ApplicationEventPublisher applicationEventPublisher;
public UserGetResponse getUserById(Long userId) {
var existUser = getUser(userId);
@@ -86,6 +89,7 @@ public void withdraw(UserWithdrawRequest request, Long userId) {
userRepository.delete(existUser);
withdrawReasonRepository.saveAll(request.toEntity());
+ applicationEventPublisher.publishEvent(new UserDeletedEvent(existUser.getId()));
}
@Transactional
diff --git a/src/test/java/net/teumteum/integration/UserIntegrationTest.java b/src/test/java/net/teumteum/integration/UserIntegrationTest.java
index bb42461..8b1503a 100644
--- a/src/test/java/net/teumteum/integration/UserIntegrationTest.java
+++ b/src/test/java/net/teumteum/integration/UserIntegrationTest.java
@@ -189,32 +189,6 @@ void Return_200_ok_with_success_make_friends() {
@DisplayName("친구 조회 API는")
class Find_friends_api {
- @Test
- @DisplayName("user의 id를 입력받으면, id에 해당하는 user의 친구 목록을 반환한다.")
- void Return_friends_when_received_user_id() {
- // given
- var me = repository.saveAndGetUser();
- var friend1 = repository.saveAndGetUser();
- var friend2 = repository.saveAndGetUser();
-
- securityContextSetting.set(me.getId());
-
- api.addFriends(VALID_TOKEN, friend1.getId());
- api.addFriends(VALID_TOKEN, friend2.getId());
-
- var expected = FriendsResponse.of(List.of(friend1, friend2));
-
- // when
- var result = api.getFriendsByUserId(VALID_TOKEN, me.getId());
-
- // then
- Assertions.assertThat(result.expectStatus().isOk()
- .expectBody(FriendsResponse.class)
- .returnResult()
- .getResponseBody())
- .usingRecursiveComparison().isEqualTo(expected);
- }
-
@Test
@DisplayName("user의 id를 입력받았을때, 친구가 한명도 없다면, 빈 목록을 반환한다.")
void Return_empty_friends_when_received_empty_friends_user_id() {
diff --git a/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java b/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
index 66b1668..1c5630a 100644
--- a/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
+++ b/src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
@@ -39,9 +39,9 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
-@ExtendWith(MockitoExtension.class)
+@ExtendWith(SpringExtension.class)
@DisplayName("유저 서비스 단위 테스트의")
public class UserServiceTest {
@@ -135,27 +135,6 @@ void If_valid_user_logout_request_return_200_OK() {
@DisplayName("회원 탈퇴 API는")
class Withdraw_user_api_unit {
- @Test
- @DisplayName("유효한 유저 회원 탈퇴 요청이 들어오는 경우, 회원을 탈퇴하고 탈퇴 사유 데이터를 저장한다.")
- void If_valid_user_withdraw_request_withdraw_user() {
- // given
- UserWithdrawRequest request
- = RequestFixture.userWithdrawRequest(List.of("쓰지 않는 앱이에요", "오류가 생겨서 쓸 수 없어요"));
-
- given(userRepository.findById(anyLong()))
- .willReturn(Optional.ofNullable(user));
-
- doNothing().when(userRepository).delete(any());
-
- doNothing().when(redisService).deleteData(anyString());
- // when
- userService.withdraw(request, user.getId());
- // then
- verify(userRepository, times(1)).findById(anyLong());
- verify(redisService, times(1)).deleteData(anyString());
- verify(withdrawReasonRepository, times(1)).saveAll(any());
- }
-
@Test
@DisplayName("유저 id에 해당하는 유저가 존재하지 않는 경우, 400 Bad Request 을 반환한다.")
void Return_400_bad_request_if_user_is_not_exist() {
From f5874bcb1dda8a3f2c47e74906c513145cec7622 Mon Sep 17 00:00:00 2001
From: xb205 <62425964+devxb@users.noreply.github.com>
Date: Fri, 16 Feb 2024 12:01:28 +0900
Subject: [PATCH 09/11] =?UTF-8?q?fix:=20=EB=AA=A8=EC=9E=84=20=EC=8B=9C?=
=?UTF-8?q?=EC=9E=91,=20=EC=A2=85=EB=A3=8C=20=EC=95=8C=EB=A6=BC=EC=9D=B4?=
=?UTF-8?q?=20=EB=AA=A8=EC=9E=84=20=EC=B0=B8=EC=97=AC=EC=9E=90=EA=B0=80=20?=
=?UTF-8?q?3=EB=AA=85=20=EC=9D=B4=EC=83=81=EC=9D=B4=20=EC=95=84=EB=8B=88?=
=?UTF-8?q?=EB=9D=BC=EB=A9=B4,=20=EB=B0=9C=ED=96=89=EB=90=98=EC=A7=80?=
=?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#231)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../service/MeetingAlertPublisher.java | 33 +++++++++++--------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java b/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
index 858650e..bcc2bd8 100644
--- a/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
+++ b/src/main/java/net/teumteum/meeting/service/MeetingAlertPublisher.java
@@ -16,6 +16,7 @@
@Transactional(readOnly = true)
public class MeetingAlertPublisher {
+ private static final String KR_TIME_ZONE = "Asia/Seoul";
private static final String EVERY_ONE_MINUTES = "0 * * * * *";
private static final String EVERY_12PM = "0 0 12 * * *";
@@ -24,18 +25,20 @@ public class MeetingAlertPublisher {
@Scheduled(cron = EVERY_ONE_MINUTES)
public void alertBeforeMeeting() {
- var alertStart = LocalDateTime.now(ZoneId.of("Asia/Seoul")).plusMinutes(5).withNano(0).withSecond(0);
+ var alertStart = LocalDateTime.now(ZoneId.of(KR_TIME_ZONE)).plusMinutes(5).withNano(0).withSecond(0);
var alertEnd = alertStart.plusMinutes(1).withNano(0).withSecond(0);
var alertTargets = meetingRepository.findAlertMeetings(alertStart, alertEnd);
- alertTargets.forEach(meeting -> eventPublisher.publishEvent(
- new BeforeMeetingAlerted(meeting.getParticipantUserIds())
- )
- );
+ alertTargets.stream()
+ .filter(alertTarget -> alertTarget.getParticipantUserIds().size() > 2)
+ .forEach(meeting -> eventPublisher.publishEvent(
+ new BeforeMeetingAlerted(meeting.getParticipantUserIds())
+ )
+ );
}
@Scheduled(cron = EVERY_12PM)
public void alertEndMeeting() {
- var today = LocalDateTime.now(ZoneId.of("Asia/Seoul"))
+ var today = LocalDateTime.now(ZoneId.of(KR_TIME_ZONE))
.withNano(0)
.withSecond(0)
.withMinute(0)
@@ -44,14 +47,16 @@ public void alertEndMeeting() {
var yesterday = today.minusDays(1);
var alertTargets = meetingRepository.findAlertMeetings(yesterday, today);
- alertTargets.forEach(meeting -> eventPublisher.publishEvent(
- new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
- ));
+ alertTargets.stream()
+ .filter(alertTarget -> alertTarget.getParticipantUserIds().size() > 2)
+ .forEach(meeting -> eventPublisher.publishEvent(
+ new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
+ ));
}
@Scheduled(cron = EVERY_ONE_MINUTES)
public void alertEndMeetingForQa() {
- var today = LocalDateTime.now(ZoneId.of("Asia/Seoul"))
+ var today = LocalDateTime.now(ZoneId.of(KR_TIME_ZONE))
.withNano(0)
.withSecond(0)
.withMinute(0)
@@ -61,8 +66,10 @@ public void alertEndMeetingForQa() {
var yesterday = today.minusDays(365);
var alertTargets = meetingRepository.findAlertMeetings(yesterday, future);
- alertTargets.forEach(meeting -> eventPublisher.publishEvent(
- new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
- ));
+ alertTargets.stream()
+ .filter(alertTarget -> alertTarget.getParticipantUserIds().size() > 2)
+ .forEach(meeting -> eventPublisher.publishEvent(
+ new EndMeetingAlerted(meeting.getId(), meeting.getTitle(), meeting.getParticipantUserIds())
+ ));
}
}
From 641ee6ea5e418e2f796dd908f29a02c4b10e2b74 Mon Sep 17 00:00:00 2001
From: ChoiDongKuen
Date: Fri, 16 Feb 2024 12:04:36 +0900
Subject: [PATCH 10/11] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EB=93=B1?=
=?UTF-8?q?=EB=A1=9D=EC=8B=9C=20=EB=AA=A8=EC=9E=84=20=EC=B0=B8=EC=97=AC=20?=
=?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EC=A1=B0=ED=9A=8C=20API=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95=20(#229)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor: MeetingController getCurrentUserId() 메소드로 통일 (#228)
* feat: 리뷰에 자신 포함 확인 로직 추가 (#195)
* feat: 모임 참여자 자신 포함 여부 확인 로직 추가(#228)
* test: 단위 테스트 추가 (#228)
* test: 통합 테스트 수정 및 실패 케이스 추가 (#228)
---
.../meeting/controller/MeetingController.java | 31 +++--
...e.java => MeetingParticipantResponse.java} | 6 +-
.../meeting/service/MeetingService.java | 18 ++-
.../integration/MeetingIntegrationTest.java | 118 +++++++++++-------
.../net/teumteum/integration/Repository.java | 7 +-
.../meeting/domain/MeetingFixture.java | 8 ++
.../controller/MeetingControllerTest.java | 112 +++++++++++++++++
.../meeting/service/MeetingServiceTest.java | 91 ++++++++++++++
.../user/controller/UserControllerTest.java | 3 +-
9 files changed, 327 insertions(+), 67 deletions(-)
rename src/main/java/net/teumteum/meeting/domain/response/{MeetingParticipantsResponse.java => MeetingParticipantResponse.java} (70%)
create mode 100644 src/test/java/net/teumteum/unit/meeting/controller/MeetingControllerTest.java
create mode 100644 src/test/java/net/teumteum/unit/meeting/service/MeetingServiceTest.java
diff --git a/src/main/java/net/teumteum/meeting/controller/MeetingController.java b/src/main/java/net/teumteum/meeting/controller/MeetingController.java
index d36a633..ca559ad 100644
--- a/src/main/java/net/teumteum/meeting/controller/MeetingController.java
+++ b/src/main/java/net/teumteum/meeting/controller/MeetingController.java
@@ -9,7 +9,7 @@
import net.teumteum.meeting.domain.Topic;
import net.teumteum.meeting.domain.request.CreateMeetingRequest;
import net.teumteum.meeting.domain.request.UpdateMeetingRequest;
-import net.teumteum.meeting.domain.response.MeetingParticipantsResponse;
+import net.teumteum.meeting.domain.response.MeetingParticipantResponse;
import net.teumteum.meeting.domain.response.MeetingResponse;
import net.teumteum.meeting.domain.response.MeetingsResponse;
import net.teumteum.meeting.model.PageDto;
@@ -43,14 +43,14 @@ public class MeetingController {
public MeetingResponse createMeeting(
@RequestPart @Valid CreateMeetingRequest meetingRequest,
@RequestPart List images) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
return meetingService.createMeeting(images, meetingRequest, userId);
}
@GetMapping("/{meetingId}")
@ResponseStatus(HttpStatus.OK)
public MeetingResponse getMeetingById(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
return meetingService.getMeetingById(meetingId, userId);
}
@@ -64,7 +64,7 @@ public PageDto getMeetingsByCondition(
@RequestParam(value = "participantUserId", required = false) Long participantUserId,
@RequestParam(value = "isBookmarked", required = false) Boolean isBookmarked,
@RequestParam(value = "searchWord", required = false) String searchWord) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
return meetingService.getMeetingsBySpecification(pageable, topic, meetingAreaStreet, participantUserId,
searchWord, isBookmarked, isOpen, userId);
}
@@ -74,55 +74,56 @@ public PageDto getMeetingsByCondition(
public MeetingResponse updateMeeting(@PathVariable Long meetingId,
@RequestPart @Valid UpdateMeetingRequest request,
@RequestPart List images) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
return meetingService.updateMeeting(meetingId, images, request, userId);
}
@DeleteMapping("/{meetingId}")
@ResponseStatus(HttpStatus.OK)
public void deleteMeeting(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
meetingService.deleteMeeting(meetingId, userId);
}
@PostMapping("/{meetingId}/participants")
@ResponseStatus(HttpStatus.CREATED)
public MeetingResponse addParticipant(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
return meetingService.addParticipant(meetingId, userId);
}
@DeleteMapping("/{meetingId}/participants")
@ResponseStatus(HttpStatus.OK)
public void deleteParticipant(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
meetingService.cancelParticipant(meetingId, userId);
}
@GetMapping("/{meetingId}/participants")
@ResponseStatus(HttpStatus.OK)
- public List getParticipants(@PathVariable("meetingId") Long meetingId) {
- return meetingService.getParticipants(meetingId);
+ public List getParticipants(@PathVariable("meetingId") Long meetingId) {
+ Long userId = getCurrentUserId();
+ return meetingService.getParticipants(meetingId, userId);
}
@PostMapping("/{meetingId}/reports")
@ResponseStatus(HttpStatus.CREATED)
public void reportMeeting(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
meetingService.reportMeeting(meetingId, userId);
}
@PostMapping("/{meetingId}/bookmarks")
@ResponseStatus(HttpStatus.CREATED)
public void addBookmark(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
meetingService.addBookmark(meetingId, userId);
}
@DeleteMapping("/{meetingId}/bookmarks")
@ResponseStatus(HttpStatus.OK)
public void deleteBookmark(@PathVariable("meetingId") Long meetingId) {
- Long userId = securityService.getCurrentUserId();
+ Long userId = getCurrentUserId();
meetingService.cancelBookmark(meetingId, userId);
}
@@ -132,4 +133,8 @@ public ErrorResponse handleIllegalArgumentException(IllegalArgumentException ill
Sentry.captureException(illegalArgumentException);
return ErrorResponse.of(illegalArgumentException);
}
+
+ private Long getCurrentUserId() {
+ return securityService.getCurrentUserId();
+ }
}
diff --git a/src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantsResponse.java b/src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantResponse.java
similarity index 70%
rename from src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantsResponse.java
rename to src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantResponse.java
index 10d9df9..055ee11 100644
--- a/src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantsResponse.java
+++ b/src/main/java/net/teumteum/meeting/domain/response/MeetingParticipantResponse.java
@@ -2,17 +2,17 @@
import net.teumteum.user.domain.User;
-public record MeetingParticipantsResponse(
+public record MeetingParticipantResponse(
Long id,
Long characterId,
String name,
String job
) {
- public static MeetingParticipantsResponse of(
+ public static MeetingParticipantResponse of(
User user
) {
- return new MeetingParticipantsResponse(
+ return new MeetingParticipantResponse(
user.getId(),
user.getCharacterId(),
user.getName(),
diff --git a/src/main/java/net/teumteum/meeting/service/MeetingService.java b/src/main/java/net/teumteum/meeting/service/MeetingService.java
index 99cfc41..5003080 100644
--- a/src/main/java/net/teumteum/meeting/service/MeetingService.java
+++ b/src/main/java/net/teumteum/meeting/service/MeetingService.java
@@ -12,7 +12,7 @@
import net.teumteum.meeting.domain.Topic;
import net.teumteum.meeting.domain.request.CreateMeetingRequest;
import net.teumteum.meeting.domain.request.UpdateMeetingRequest;
-import net.teumteum.meeting.domain.response.MeetingParticipantsResponse;
+import net.teumteum.meeting.domain.response.MeetingParticipantResponse;
import net.teumteum.meeting.domain.response.MeetingResponse;
import net.teumteum.meeting.domain.response.MeetingsResponse;
import net.teumteum.meeting.model.PageDto;
@@ -94,7 +94,8 @@ public void deleteMeeting(Long meetingId, Long userId) {
}
@Transactional(readOnly = true)
- public PageDto getMeetingsBySpecification(Pageable pageable, Topic topic, String meetingAreaStreet,
+ public PageDto getMeetingsBySpecification(Pageable pageable, Topic topic,
+ String meetingAreaStreet,
Long participantUserId, String searchWord, Boolean isBookmarked, Boolean isOpen, Long userId) {
Specification spec = MeetingSpecification.withIsOpen(isOpen);
@@ -153,13 +154,16 @@ public void cancelParticipant(Long meetingId, Long userId) {
}
@Transactional(readOnly = true)
- public List getParticipants(Long meetingId) {
+ public List getParticipants(Long meetingId, Long userId) {
var existMeeting = getMeeting(meetingId);
+ checkMeetingContainUser(existMeeting, userId);
+
return existMeeting.getParticipantUserIds().stream()
+ .filter(id -> !id.equals(userId))
.map(userConnector::findUserById)
.flatMap(Optional::stream)
- .map(MeetingParticipantsResponse::of)
+ .map(MeetingParticipantResponse::of)
.toList();
}
@@ -207,4 +211,10 @@ public void reportMeeting(Long meetingId, Long userId) {
throw new IllegalArgumentException("모임 개설자는 모임을 신고할 수 없습니다.");
}
}
+
+ private void checkMeetingContainUser(Meeting meeting, Long userId) {
+ if (!meeting.getParticipantUserIds().contains(userId)) {
+ throw new IllegalArgumentException("모임에 참여하지 않은 회원입니다.");
+ }
+ }
}
diff --git a/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java b/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java
index dc45f2c..27d76d2 100644
--- a/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java
+++ b/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java
@@ -1,7 +1,10 @@
package net.teumteum.integration;
+import static org.assertj.core.api.Assertions.assertThat;
+
import java.util.Collection;
import java.util.Comparator;
+import java.util.List;
import java.util.stream.Stream;
import net.teumteum.core.error.ErrorResponse;
import net.teumteum.meeting.domain.Meeting;
@@ -9,7 +12,6 @@
import net.teumteum.meeting.domain.response.MeetingResponse;
import net.teumteum.meeting.domain.response.MeetingsResponse;
import net.teumteum.meeting.model.PageDto;
-import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@@ -42,10 +44,10 @@ void Return_meeting_info_if_exist_meeting_id_received() {
// when
var result = api.getMeetingById(VALID_TOKEN, meeting.getId());
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(MeetingResponse.class)
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(MeetingResponse.class)
+ .returnResult().getResponseBody())
.usingRecursiveComparison()
.isEqualTo(expected);
}
@@ -75,10 +77,10 @@ void Return_is_bookmarked_true_if_user_bookmarked_meeting() {
// when
var result = api.getMeetingById(VALID_TOKEN, meeting.getId());
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(MeetingResponse.class)
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(MeetingResponse.class)
+ .returnResult().getResponseBody())
.extracting(MeetingResponse::isBookmarked)
.isEqualTo(true);
}
@@ -158,11 +160,11 @@ void Return_meeting_list_if_topic_and_page_nation_received() {
// when
var result = api.getMeetingsByTopic(VALID_TOKEN, FIRST_PAGE_NATION, true, Topic.스터디);
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(new ParameterizedTypeReference>() {
- })
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(new ParameterizedTypeReference>() {
+ })
+ .returnResult().getResponseBody())
.usingRecursiveComparison()
.isEqualTo(expected);
}
@@ -192,11 +194,11 @@ void Return_meeting_list_if_search_word_and_page_nation_received() {
// when
var result = api.getMeetingsByTopic(VALID_TOKEN, FIRST_PAGE_NATION, true, Topic.스터디);
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(new ParameterizedTypeReference>() {
- })
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(new ParameterizedTypeReference>() {
+ })
+ .returnResult().getResponseBody())
.usingRecursiveComparison()
.isEqualTo(expected);
}
@@ -222,11 +224,11 @@ void Return_meeting_list_if_participant_user_id_and_page_nation_received() {
// when
var result = api.getMeetingsByTopic(VALID_TOKEN, FIRST_PAGE_NATION, true, Topic.스터디);
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(new ParameterizedTypeReference>() {
- })
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(new ParameterizedTypeReference>() {
+ })
+ .returnResult().getResponseBody())
.usingRecursiveComparison()
.isEqualTo(expected);
}
@@ -252,11 +254,11 @@ void Return_has_next_true_if_more_data_exists_than_requested_size_and_page() {
// when
var result = api.getMeetingsByTopic(VALID_TOKEN, FIRST_PAGE_NATION, true, Topic.스터디);
// then
- Assertions.assertThat(
- result.expectStatus().isOk()
- .expectBody(new ParameterizedTypeReference>() {
- })
- .returnResult().getResponseBody())
+ assertThat(
+ result.expectStatus().isOk()
+ .expectBody(new ParameterizedTypeReference>() {
+ })
+ .returnResult().getResponseBody())
.usingRecursiveComparison()
.isEqualTo(expected);
}
@@ -277,11 +279,11 @@ void Join_meeting_if_exist_meeting_id_received() {
// when
var result = api.joinMeeting(VALID_TOKEN, existMeeting.getId());
// then
- Assertions.assertThat(
- result.expectStatus().isCreated()
- .expectBody(MeetingResponse.class)
- .returnResult()
- .getResponseBody())
+ assertThat(
+ result.expectStatus().isCreated()
+ .expectBody(MeetingResponse.class)
+ .returnResult()
+ .getResponseBody())
.extracting(MeetingResponse::participantIds)
.has(new Condition<>(ids -> ids.contains(me.getId()), "참여자 목록에 나를 포함한다.")
);
@@ -380,11 +382,11 @@ void Return_400_bad_request_if_not_joined_meeting_id_received() {
// when
var result = api.cancelMeeting(VALID_TOKEN, meeting.getId());
// then
- Assertions.assertThat(result.expectStatus().isBadRequest()
- .expectBody(ErrorResponse.class)
- .returnResult()
- .getResponseBody()
- )
+ assertThat(result.expectStatus().isBadRequest()
+ .expectBody(ErrorResponse.class)
+ .returnResult()
+ .getResponseBody()
+ )
.extracting(ErrorResponse::getMessage)
.isEqualTo("참여하지 않은 모임입니다.");
}
@@ -400,11 +402,11 @@ void Return_400_bad_request_if_closed_meeting_id_received() {
// when
var result = api.cancelMeeting(VALID_TOKEN, meeting.getId());
// then
- Assertions.assertThat(result.expectStatus().isBadRequest()
- .expectBody(ErrorResponse.class)
- .returnResult()
- .getResponseBody()
- )
+ assertThat(result.expectStatus().isBadRequest()
+ .expectBody(ErrorResponse.class)
+ .returnResult()
+ .getResponseBody()
+ )
.extracting(ErrorResponse::getMessage)
.isEqualTo("종료된 모임에서 참여를 취소할 수 없습니다.");
}
@@ -488,11 +490,37 @@ class Get_meeting_participants_api {
@DisplayName("참여한 meeting id 가 주어지면, 참여한 참가자들의 정보가 주어진다.")
void Get_participants_if_exist_meeting_id_received() {
// given
- var meeting = repository.saveAndGetOpenMeeting();
+ var me = repository.saveAndGetUser();
+ var meeting = repository.saveAndGetClosedMetingWithParticipantUserIds(List.of(me.getId(), 2L));
+
+ securityContextSetting.set(me.getId());
+
// when
var result = api.getMeetingParticipants(VALID_TOKEN, meeting.getId());
+
// then
result.expectStatus().isOk();
}
+
+ @Test
+ @DisplayName("API 호출한 회원이 모임에 참여하지 않았다면, 400 bad request 을 응답한다.")
+ void Return_400_bad_request_if_meeting_not_contain_user() {
+ // given
+ var me = repository.saveAndGetUser();
+ var meeting = repository.saveAndGetClosedMetingWithParticipantUserIds(List.of(100L, 101L));
+
+ securityContextSetting.set(me.getId());
+
+ // when
+ var result = api.getMeetingParticipants(VALID_TOKEN, meeting.getId());
+
+ // then
+ assertThat(result.expectStatus().isBadRequest()
+ .expectBody(ErrorResponse.class)
+ .returnResult()
+ .getResponseBody())
+ .extracting(ErrorResponse::getMessage)
+ .isEqualTo("모임에 참여하지 않은 회원입니다.");
+ }
}
}
diff --git a/src/test/java/net/teumteum/integration/Repository.java b/src/test/java/net/teumteum/integration/Repository.java
index 7d829ee..894c036 100644
--- a/src/test/java/net/teumteum/integration/Repository.java
+++ b/src/test/java/net/teumteum/integration/Repository.java
@@ -2,7 +2,6 @@
import java.util.List;
-import java.util.stream.IntStream;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import net.teumteum.core.config.AppConfig;
@@ -82,6 +81,11 @@ Meeting saveAndGetOpenFullMeeting() {
return meetingRepository.saveAndFlush(meeting);
}
+ Meeting saveAndGetClosedMetingWithParticipantUserIds(List participantUserIds) {
+ var meeting = MeetingFixture.getCloseMeetingWithParticipantUserIds(participantUserIds);
+ return meetingRepository.saveAndFlush(meeting);
+ }
+
List saveAndGetOpenMeetingsByTopic(int size, Topic topic) {
var meetings = Stream.generate(() -> MeetingFixture.getOpenMeetingWithTopic(topic))
.limit(size)
@@ -146,6 +150,7 @@ List saveAndGetOpenMeetings(int size) {
return meetingRepository.saveAllAndFlush(meetings);
}
+
void clear() {
userRepository.deleteAll();
meetingRepository.deleteAll();
diff --git a/src/test/java/net/teumteum/meeting/domain/MeetingFixture.java b/src/test/java/net/teumteum/meeting/domain/MeetingFixture.java
index 93f1da2..09b850e 100644
--- a/src/test/java/net/teumteum/meeting/domain/MeetingFixture.java
+++ b/src/test/java/net/teumteum/meeting/domain/MeetingFixture.java
@@ -82,6 +82,14 @@ public static Meeting getCloseMeetingWithParticipantUserId(Long participantUserI
);
}
+ public static Meeting getCloseMeetingWithParticipantUserIds(List participantUserIds) {
+ return newMeetingByBuilder(MeetingBuilder.builder()
+ .promiseDateTime(LocalDateTime.of(2000, 1, 1, 0, 0))
+ .participantUserIds(new HashSet<>(participantUserIds))
+ .build()
+ );
+ }
+
public static Meeting getOpenMeetingWithTitle(String title) {
return newMeetingByBuilder(MeetingBuilder.builder()
.promiseDateTime(LocalDateTime.of(4000, 1, 1, 0, 0))
diff --git a/src/test/java/net/teumteum/unit/meeting/controller/MeetingControllerTest.java b/src/test/java/net/teumteum/unit/meeting/controller/MeetingControllerTest.java
new file mode 100644
index 0000000..da166b3
--- /dev/null
+++ b/src/test/java/net/teumteum/unit/meeting/controller/MeetingControllerTest.java
@@ -0,0 +1,112 @@
+package net.teumteum.unit.meeting.controller;
+
+import static net.teumteum.unit.common.SecurityValue.VALID_ACCESS_TOKEN;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.BDDMockito.given;
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.List;
+import net.teumteum.core.security.SecurityConfig;
+import net.teumteum.core.security.filter.JwtAuthenticationFilter;
+import net.teumteum.core.security.service.JwtService;
+import net.teumteum.core.security.service.RedisService;
+import net.teumteum.core.security.service.SecurityService;
+import net.teumteum.meeting.controller.MeetingController;
+import net.teumteum.meeting.domain.response.MeetingParticipantResponse;
+import net.teumteum.meeting.service.MeetingService;
+import net.teumteum.user.domain.UserFixture;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.FilterType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+
+@WebMvcTest(value = MeetingController.class,
+ excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class),
+ @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtAuthenticationFilter.class),
+ @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = RedisService.class),
+ @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtService.class)}
+)
+@WithMockUser
+@DisplayName("모임 컨트롤러 단위 테스트의")
+public class MeetingControllerTest {
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private MeetingService meetingService;
+
+ @MockBean
+ private SecurityService securityService;
+
+ @Nested
+ @DisplayName("모임 참여자 조회 API는")
+ class Get_meeting_participants_api_unit {
+
+ @Test
+ @DisplayName("API 호출 회원을 제외한, meetingId 해당하는 모임의 참여자 정보를 반환한다.")
+ void Return_meeting_participants_with_200_ok() throws Exception {
+ // given
+ var existUser1 = UserFixture.getUserWithId(1L);
+ var existUser2 = UserFixture.getUserWithId(2L);
+ var existUser3 = UserFixture.getUserWithId(3L);
+
+ List response
+ = List.of(MeetingParticipantResponse.of(existUser2), MeetingParticipantResponse.of(existUser3));
+
+ given(securityService.getCurrentUserId())
+ .willReturn(existUser1.getId());
+
+ given(meetingService.getParticipants(anyLong(), anyLong()))
+ .willReturn(response);
+
+ // when && then
+ mockMvc.perform(get("/meetings/{meetingId}/participants", 2L)
+ .with(csrf())
+ .header(AUTHORIZATION, VALID_ACCESS_TOKEN))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isNotEmpty())
+ .andExpect(jsonPath("$.length()").value(2))
+ .andExpect(jsonPath("$").isArray())
+ .andExpect(jsonPath("$[0].id").value(2))
+ .andExpect(jsonPath("$[0].characterId").value(1));
+ }
+
+ @Test
+ @DisplayName("모임에 API 호출 회원이 존재하지 않는 경우, 400 bad request를 응답한다.")
+ void Return_400_bad_request_if_meeting_not_contain_user() throws Exception {
+ // given
+ var existUser1 = UserFixture.getUserWithId(1L);
+
+ given(securityService.getCurrentUserId())
+ .willReturn(existUser1.getId());
+
+ given(meetingService.getParticipants(anyLong(), anyLong())).willThrow(
+ new IllegalArgumentException("모임에 참여하지 않은 회원입니다."));
+
+ // when & then
+ mockMvc.perform(get("/meetings/{meetingId}/participants", 2L)
+ .with(csrf())
+ .header(AUTHORIZATION, VALID_ACCESS_TOKEN))
+ .andDo(print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.message").value("모임에 참여하지 않은 회원입니다."));
+ }
+ }
+}
diff --git a/src/test/java/net/teumteum/unit/meeting/service/MeetingServiceTest.java b/src/test/java/net/teumteum/unit/meeting/service/MeetingServiceTest.java
new file mode 100644
index 0000000..c6cd397
--- /dev/null
+++ b/src/test/java/net/teumteum/unit/meeting/service/MeetingServiceTest.java
@@ -0,0 +1,91 @@
+package net.teumteum.unit.meeting.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.BDDMockito.given;
+
+import java.util.List;
+import java.util.Optional;
+import net.teumteum.meeting.domain.ImageUpload;
+import net.teumteum.meeting.domain.MeetingFixture;
+import net.teumteum.meeting.domain.MeetingRepository;
+import net.teumteum.meeting.domain.response.MeetingParticipantResponse;
+import net.teumteum.meeting.service.MeetingService;
+import net.teumteum.user.domain.UserConnector;
+import net.teumteum.user.domain.UserFixture;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+@DisplayName("모임 서비스 단위 테스트의")
+public class MeetingServiceTest {
+
+ @InjectMocks
+ MeetingService meetingService;
+
+ @Mock
+ MeetingRepository meetingRepository;
+
+ @Mock
+ UserConnector userConnector;
+
+ @Mock
+ ImageUpload imageUpload;
+
+ @Nested
+ @DisplayName("모임 참여자 조회 API는")
+ class Return_meeting_participants_api_unit {
+
+ @Test
+ @DisplayName("API 호출 회원을 제외한, meetingId 해당하는 모임의 참여자 정보를 반환한다.")
+ void Return_meeting_participants_with_200_ok() {
+ // given
+ var userId = 1L;
+ var meetingId = 1L;
+
+ var existMeeting
+ = MeetingFixture.getCloseMeetingWithParticipantUserIds(List.of(userId, 2L, 3L));
+
+ var existUser2 = UserFixture.getUserWithId(2L);
+ var existUser3 = UserFixture.getUserWithId(3L);
+
+ given(meetingRepository.findById(anyLong()))
+ .willReturn(Optional.of(existMeeting));
+
+ given(userConnector.findUserById(existUser2.getId())).willReturn(Optional.of(existUser2));
+ given(userConnector.findUserById(existUser3.getId())).willReturn(Optional.of(existUser3));
+
+ // when
+ List participants = meetingService.getParticipants(meetingId, userId);
+ // then
+ assertThat(participants).hasSize(2);
+ }
+
+ @Test
+ @DisplayName("모임에 API 호출 회원이 존재하지 않는 경우, 400 bad request를 응답한다.")
+ void Return_400_bad_request_if_meeting_not_contain_user() {
+ // given
+ var userId = 1L;
+ var notContainedUserId = 4L;
+
+ var meetingId = 1L;
+
+ var existMeeting
+ = MeetingFixture.getCloseMeetingWithParticipantUserIds(List.of(userId, 2L, 3L));
+
+ given(meetingRepository.findById(anyLong()))
+ .willReturn(Optional.of(existMeeting));
+
+ // when
+ assertThatThrownBy(() -> meetingService.getParticipants(meetingId, notContainedUserId))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("모임에 참여하지 않은 회원입니다.");
+ }
+ }
+}
diff --git a/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java b/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
index 77afd49..2b1d877 100644
--- a/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
+++ b/src/test/java/net/teumteum/unit/user/controller/UserControllerTest.java
@@ -63,10 +63,11 @@
public class UserControllerTest {
@Autowired
- ObjectMapper objectMapper;
+ private ObjectMapper objectMapper;
@Autowired
private MockMvc mockMvc;
+
@MockBean
private UserService userService;
From 9cffa3392069f8080193e5b6388a4c5f146f70f4 Mon Sep 17 00:00:00 2001
From: devxb
Date: Fri, 16 Feb 2024 12:07:56 +0900
Subject: [PATCH 11/11] =?UTF-8?q?refactor:=20title,=20body=20alert=20data?=
=?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.java b/src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.java
index 7b9c830..68ae10d 100644
--- a/src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.java
+++ b/src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.java
@@ -44,6 +44,8 @@ private Message buildMessage(String token, Alert alert, Map data
.setToken(token)
.setNotification(buildNotification(alert))
.setAndroidConfig(buildAndroidConfig(alert))
+ .putData("title", alert.getTitle())
+ .putData("body", alert.getBody())
.putData("publishedAt", alert.getCreatedAt().toString())
.putData("userId", alert.getUserId().toString())
.putData("type", alert.getType().toString())