Skip to content

Commit

Permalink
Merge pull request #174 from minahYu/feat/get-friend-requests
Browse files Browse the repository at this point in the history
[User] 친구 신청 조회 API 구현
  • Loading branch information
minahYu authored Jun 18, 2024
2 parents c62fda8 + e4ee4fa commit 45630c4
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 13 deletions.
25 changes: 19 additions & 6 deletions user/src/main/kotlin/kpring/user/controller/FriendController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<ApiResponse<AddFriendResponse>> {
): ResponseEntity<ApiResponse<GetFriendRequestsResponse>> {
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))
}

Expand All @@ -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<ApiResponse<AddFriendResponse>> {
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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package kpring.user.dto.result
package kpring.user.dto.response

data class AddFriendResponse(
val friendId: Long,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kpring.user.dto.response

data class GetFriendRequestResponse(
val friendId: Long,
val username: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kpring.user.dto.response

data class GetFriendRequestsResponse(
val userId: Long,
var friendRequests: List<GetFriendRequestResponse>,
)
2 changes: 1 addition & 1 deletion user/src/main/kotlin/kpring/user/entity/Friend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package kpring.user.repository

import kpring.user.entity.Friend
import kpring.user.entity.FriendRequestStatus
import org.springframework.data.jpa.repository.JpaRepository

interface FriendRepository : JpaRepository<Friend, Long> {
fun existsByUserIdAndFriendId(
userId: Long,
friendId: Long,
): Boolean

fun findAllByUserIdAndRequestStatus(
userId: Long,
requestStatus: FriendRequestStatus,
): List<Friend>
}
5 changes: 4 additions & 1 deletion user/src/main/kotlin/kpring/user/service/FriendService.kt
Original file line number Diff line number Diff line change
@@ -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(
Expand Down
20 changes: 17 additions & 3 deletions user/src/main/kotlin/kpring/user/service/FriendServiceImpl.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
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
class FriendServiceImpl(
private val userServiceImpl: UserServiceImpl,
private val friendRepository: FriendRepository,
) : FriendService {
override fun getFriendRequests(userId: Long): GetFriendRequestsResponse {
val friendRelations: List<Friend> =
friendRepository.findAllByUserIdAndRequestStatus(userId, FriendRequestStatus.RECEIVED)
val friendRequests: MutableList<GetFriendRequestResponse> = 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")
}
Expand Down
152 changes: 151 additions & 1 deletion user/src/test/kotlin/kpring/user/controller/FriendControllerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 "에러 메시지"
}
}
}
}
}
})
1 change: 1 addition & 0 deletions user/src/test/kotlin/kpring/user/global/CommonTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ interface CommonTest {
const val TEST_TOKEN = "Bearer test"

const val TEST_FRIEND_ID = 2L
const val TEST_FRIEND_USERNAME = "friend"
}
}
17 changes: 17 additions & 0 deletions user/src/test/kotlin/kpring/user/service/FriendServiceImplTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -99,4 +101,19 @@ internal class FriendServiceImplTest : FunSpec({
friendService.checkFriendRelationExists(CommonTest.TEST_USER_ID, CommonTest.TEST_FRIEND_ID)
}
}

test("친구신청조회_성공") {
val friend = mockk<User>(relaxed = true)
val friendList = listOf(mockk<Friend>(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
}
}
})

0 comments on commit 45630c4

Please sign in to comment.