diff --git a/user/src/main/kotlin/kpring/user/controller/FriendController.kt b/user/src/main/kotlin/kpring/user/controller/FriendController.kt index 25600e73..094672a9 100644 --- a/user/src/main/kotlin/kpring/user/controller/FriendController.kt +++ b/user/src/main/kotlin/kpring/user/controller/FriendController.kt @@ -2,9 +2,10 @@ package kpring.user.controller import kpring.core.auth.client.AuthClient import kpring.core.global.dto.response.ApiResponse +import kpring.user.dto.response.AddFriendResponse import kpring.user.dto.response.DeleteFriendResponse +import kpring.user.dto.response.GetFriendRequestsResponse import kpring.user.dto.response.GetFriendsResponse -import kpring.user.dto.result.AddFriendResponse import kpring.user.global.AuthValidator import kpring.user.service.FriendService import org.springframework.http.ResponseEntity @@ -17,16 +18,15 @@ class FriendController( private val authValidator: AuthValidator, private val authClient: AuthClient, ) { - @PostMapping("/user/{userId}/friend/{friendId}") - fun addFriend( + @GetMapping("/user/{userId}/requests") + fun getFriendRequests( @RequestHeader("Authorization") token: String, @PathVariable userId: Long, - @PathVariable friendId: Long, - ): ResponseEntity> { + ): ResponseEntity> { val validationResult = authClient.getTokenInfo(token) val validatedUserId = authValidator.checkIfAccessTokenAndGetUserId(validationResult) authValidator.checkIfUserIsSelf(userId.toString(), validatedUserId) - val response = friendService.addFriend(userId, friendId) + val response = friendService.getFriendRequests(userId) return ResponseEntity.ok(ApiResponse(data = response)) } @@ -41,6 +41,19 @@ class FriendController( return ResponseEntity.ok(ApiResponse(data = response)) } + @PostMapping("/user/{userId}/friend/{friendId}") + fun addFriend( + @RequestHeader("Authorization") token: String, + @PathVariable userId: Long, + @PathVariable friendId: Long, + ): ResponseEntity> { + val validationResult = authClient.getTokenInfo(token) + val validatedUserId = authValidator.checkIfAccessTokenAndGetUserId(validationResult) + authValidator.checkIfUserIsSelf(userId.toString(), validatedUserId) + val response = friendService.addFriend(userId, friendId) + return ResponseEntity.ok(ApiResponse(data = response)) + } + @DeleteMapping("/user/{userId}/friend/{friendId}") fun deleteFriend( @RequestHeader("Authorization") token: String, diff --git a/user/src/main/kotlin/kpring/user/dto/result/AddFriendResponse.kt b/user/src/main/kotlin/kpring/user/dto/response/AddFriendResponse.kt similarity index 62% rename from user/src/main/kotlin/kpring/user/dto/result/AddFriendResponse.kt rename to user/src/main/kotlin/kpring/user/dto/response/AddFriendResponse.kt index b479cbb5..73508dcf 100644 --- a/user/src/main/kotlin/kpring/user/dto/result/AddFriendResponse.kt +++ b/user/src/main/kotlin/kpring/user/dto/response/AddFriendResponse.kt @@ -1,4 +1,4 @@ -package kpring.user.dto.result +package kpring.user.dto.response data class AddFriendResponse( val friendId: Long, diff --git a/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestResponse.kt b/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestResponse.kt new file mode 100644 index 00000000..2ca82ac2 --- /dev/null +++ b/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestResponse.kt @@ -0,0 +1,6 @@ +package kpring.user.dto.response + +data class GetFriendRequestResponse( + val friendId: Long, + val username: String, +) diff --git a/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestsResponse.kt b/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestsResponse.kt new file mode 100644 index 00000000..879ef532 --- /dev/null +++ b/user/src/main/kotlin/kpring/user/dto/response/GetFriendRequestsResponse.kt @@ -0,0 +1,6 @@ +package kpring.user.dto.response + +data class GetFriendRequestsResponse( + val userId: Long, + var friendRequests: List, +) diff --git a/user/src/main/kotlin/kpring/user/entity/Friend.kt b/user/src/main/kotlin/kpring/user/entity/Friend.kt index fd973685..da9aeab5 100644 --- a/user/src/main/kotlin/kpring/user/entity/Friend.kt +++ b/user/src/main/kotlin/kpring/user/entity/Friend.kt @@ -13,7 +13,7 @@ class Friend( private var user: User, @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "friend_id") - private var friend: User, + var friend: User, @Enumerated(EnumType.STRING) @Column(nullable = false) var requestStatus: FriendRequestStatus, diff --git a/user/src/main/kotlin/kpring/user/repository/FriendRepository.kt b/user/src/main/kotlin/kpring/user/repository/FriendRepository.kt index ae0173b4..ee5bc406 100644 --- a/user/src/main/kotlin/kpring/user/repository/FriendRepository.kt +++ b/user/src/main/kotlin/kpring/user/repository/FriendRepository.kt @@ -1,6 +1,7 @@ package kpring.user.repository import kpring.user.entity.Friend +import kpring.user.entity.FriendRequestStatus import org.springframework.data.jpa.repository.JpaRepository interface FriendRepository : JpaRepository { @@ -8,4 +9,9 @@ interface FriendRepository : JpaRepository { userId: Long, friendId: Long, ): Boolean + + fun findAllByUserIdAndRequestStatus( + userId: Long, + requestStatus: FriendRequestStatus, + ): List } diff --git a/user/src/main/kotlin/kpring/user/service/FriendService.kt b/user/src/main/kotlin/kpring/user/service/FriendService.kt index 8dee2657..aa903558 100644 --- a/user/src/main/kotlin/kpring/user/service/FriendService.kt +++ b/user/src/main/kotlin/kpring/user/service/FriendService.kt @@ -1,10 +1,13 @@ package kpring.user.service +import kpring.user.dto.response.AddFriendResponse import kpring.user.dto.response.DeleteFriendResponse +import kpring.user.dto.response.GetFriendRequestsResponse import kpring.user.dto.response.GetFriendsResponse -import kpring.user.dto.result.AddFriendResponse interface FriendService { + fun getFriendRequests(userId: Long): GetFriendRequestsResponse + fun getFriends(userId: Long): GetFriendsResponse fun addFriend( diff --git a/user/src/main/kotlin/kpring/user/service/FriendServiceImpl.kt b/user/src/main/kotlin/kpring/user/service/FriendServiceImpl.kt index 3c90e16f..d1d7e16a 100644 --- a/user/src/main/kotlin/kpring/user/service/FriendServiceImpl.kt +++ b/user/src/main/kotlin/kpring/user/service/FriendServiceImpl.kt @@ -1,14 +1,15 @@ package kpring.user.service import kpring.core.global.exception.ServiceException -import kpring.user.dto.response.DeleteFriendResponse -import kpring.user.dto.response.GetFriendsResponse -import kpring.user.dto.result.AddFriendResponse +import kpring.user.dto.response.* +import kpring.user.entity.Friend +import kpring.user.entity.FriendRequestStatus import kpring.user.entity.User import kpring.user.exception.UserErrorCode import kpring.user.repository.FriendRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +import java.util.stream.Collectors @Service @Transactional @@ -16,6 +17,19 @@ class FriendServiceImpl( private val userServiceImpl: UserServiceImpl, private val friendRepository: FriendRepository, ) : FriendService { + override fun getFriendRequests(userId: Long): GetFriendRequestsResponse { + val friendRelations: List = + friendRepository.findAllByUserIdAndRequestStatus(userId, FriendRequestStatus.RECEIVED) + val friendRequests: MutableList = mutableListOf() + + friendRelations.stream().map { friendRelation -> + val friend = friendRelation.friend + GetFriendRequestResponse(friend.id!!, friend.username) + }.collect(Collectors.toList()) + + return GetFriendRequestsResponse(userId, friendRequests) + } + override fun getFriends(userId: Long): GetFriendsResponse { TODO("Not yet implemented") } diff --git a/user/src/test/kotlin/kpring/user/controller/FriendControllerTest.kt b/user/src/test/kotlin/kpring/user/controller/FriendControllerTest.kt index 9377e36a..0367a6bf 100644 --- a/user/src/test/kotlin/kpring/user/controller/FriendControllerTest.kt +++ b/user/src/test/kotlin/kpring/user/controller/FriendControllerTest.kt @@ -11,9 +11,12 @@ import kpring.core.auth.enums.TokenType import kpring.core.global.dto.response.ApiResponse import kpring.core.global.exception.ServiceException import kpring.test.restdoc.dsl.restDoc +import kpring.test.restdoc.json.JsonDataType import kpring.test.restdoc.json.JsonDataType.Strings +import kpring.user.dto.response.AddFriendResponse import kpring.user.dto.response.FailMessageResponse -import kpring.user.dto.result.AddFriendResponse +import kpring.user.dto.response.GetFriendRequestResponse +import kpring.user.dto.response.GetFriendRequestsResponse import kpring.user.exception.UserErrorCode import kpring.user.global.AuthValidator import kpring.user.global.CommonTest @@ -194,4 +197,151 @@ internal class FriendControllerTest( } } } + + describe("친구신청 조회 API") { + it("친구신청 조회 성공") { + // given + val friendRequest = + GetFriendRequestResponse( + friendId = CommonTest.TEST_FRIEND_ID, + username = CommonTest.TEST_FRIEND_USERNAME, + ) + val data = + GetFriendRequestsResponse( + userId = CommonTest.TEST_USER_ID, + friendRequests = mutableListOf(friendRequest), + ) + val response = ApiResponse(data = data) + + every { authClient.getTokenInfo(any()) }.returns( + ApiResponse(data = TokenInfo(TokenType.ACCESS, CommonTest.TEST_USER_ID.toString())), + ) + every { + authValidator.checkIfAccessTokenAndGetUserId(any()) + } returns CommonTest.TEST_USER_ID.toString() + every { authValidator.checkIfUserIsSelf(any(), any()) } returns Unit + every { + friendService.getFriendRequests(CommonTest.TEST_USER_ID) + } returns data + + // when + val result = + webTestClient.get() + .uri( + "/api/v1/user/{userId}/requests", + CommonTest.TEST_USER_ID, + ) + .header("Authorization", CommonTest.TEST_TOKEN) + .exchange() + + // then + val docsRoot = + result + .expectStatus().isOk + .expectBody().json(objectMapper.writeValueAsString(response)) + + // docs + docsRoot + .restDoc( + identifier = "getFriendRequests200", + description = "친구신청 조회 API", + ) { + request { + path { + "userId" mean "사용자 아이디" + } + header { "Authorization" mean "Bearer token" } + } + response { + body { + "data.userId" type Strings mean "사용자 아이디" + "data.friendRequests" type JsonDataType.Arrays mean "친구신청한 사용자 리스트" + "data.friendRequests[].friendId" type Strings mean "친구신청한 사용자 아이디" + "data.friendRequests[].username" type Strings mean "친구신청한 사용자 닉네임" + } + } + } + } + it("친구신청 조회 실패 : 권한이 없는 토큰") { + // given + val response = + FailMessageResponse.builder().message(UserErrorCode.NOT_ALLOWED.message()).build() + every { authClient.getTokenInfo(any()) } throws ServiceException(UserErrorCode.NOT_ALLOWED) + + // when + val result = + webTestClient.get() + .uri( + "/api/v1/user/{userId}/requests", + CommonTest.TEST_USER_ID, + ) + .header("Authorization", CommonTest.TEST_TOKEN) + .exchange() + + // then + val docsRoot = + result + .expectStatus().isForbidden + .expectBody().json(objectMapper.writeValueAsString(response)) + + // docs + docsRoot + .restDoc( + identifier = "getFriendRequests403", + description = "친구신청 조회 API", + ) { + request { + path { + "userId" mean "사용자 아이디" + } + header { "Authorization" mean "Bearer token" } + } + response { + body { + "message" type Strings mean "에러 메시지" + } + } + } + } + it("친구신청 조회 실패 : 서버 내부 오류") { + // given + val response = FailMessageResponse.serverError + every { authClient.getTokenInfo(any()) } throws RuntimeException("서버 내부 오류") + + // when + val result = + webTestClient.get() + .uri( + "/api/v1/user/{userId}/requests", + CommonTest.TEST_USER_ID, + ) + .header("Authorization", CommonTest.TEST_TOKEN) + .exchange() + + // then + val docsRoot = + result + .expectStatus().isEqualTo(500) + .expectBody().json(objectMapper.writeValueAsString(response)) + + // docs + docsRoot + .restDoc( + identifier = "getFriendRequests500", + description = "친구신청 조회 API", + ) { + request { + path { + "userId" mean "사용자 아이디" + } + header { "Authorization" mean "Bearer token" } + } + response { + body { + "message" type Strings mean "에러 메시지" + } + } + } + } + } }) diff --git a/user/src/test/kotlin/kpring/user/global/CommonTest.kt b/user/src/test/kotlin/kpring/user/global/CommonTest.kt index 80716bf7..9c550963 100644 --- a/user/src/test/kotlin/kpring/user/global/CommonTest.kt +++ b/user/src/test/kotlin/kpring/user/global/CommonTest.kt @@ -10,5 +10,6 @@ interface CommonTest { const val TEST_TOKEN = "Bearer test" const val TEST_FRIEND_ID = 2L + const val TEST_FRIEND_USERNAME = "friend" } } diff --git a/user/src/test/kotlin/kpring/user/service/FriendServiceImplTest.kt b/user/src/test/kotlin/kpring/user/service/FriendServiceImplTest.kt index b22d6861..e5a24eaa 100644 --- a/user/src/test/kotlin/kpring/user/service/FriendServiceImplTest.kt +++ b/user/src/test/kotlin/kpring/user/service/FriendServiceImplTest.kt @@ -5,6 +5,8 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.mockk.* import kpring.core.global.exception.ServiceException +import kpring.user.entity.Friend +import kpring.user.entity.FriendRequestStatus import kpring.user.entity.User import kpring.user.exception.UserErrorCode import kpring.user.global.CommonTest @@ -99,4 +101,19 @@ internal class FriendServiceImplTest : FunSpec({ friendService.checkFriendRelationExists(CommonTest.TEST_USER_ID, CommonTest.TEST_FRIEND_ID) } } + + test("친구신청조회_성공") { + val friend = mockk(relaxed = true) + val friendList = listOf(mockk(relaxed = true)) + + every { + friendRepository.findAllByUserIdAndRequestStatus(CommonTest.TEST_USER_ID, FriendRequestStatus.RECEIVED) + } returns friendList + + val response = friendService.getFriendRequests(CommonTest.TEST_USER_ID) + for (request in response.friendRequests) { + request.friendId shouldBe friend.id + request.username shouldBe friend.username + } + } })