diff --git a/chat/src/main/kotlin/kpring/chat/chat/api/v1/ChatController.kt b/chat/src/main/kotlin/kpring/chat/chat/api/v1/ChatController.kt index 67d95c25..770a971a 100644 --- a/chat/src/main/kotlin/kpring/chat/chat/api/v1/ChatController.kt +++ b/chat/src/main/kotlin/kpring/chat/chat/api/v1/ChatController.kt @@ -1,8 +1,14 @@ package kpring.chat.chat.api.v1 import kpring.chat.chat.service.ChatService +import kpring.chat.global.exception.ErrorCode +import kpring.chat.global.exception.GlobalException import kpring.core.auth.client.AuthClient +import kpring.core.chat.chat.dto.request.ChatType import kpring.core.chat.chat.dto.request.CreateChatRequest +import kpring.core.global.dto.response.ApiResponse +import kpring.core.server.client.ServerClient +import kpring.core.server.dto.request.GetServerCondition import org.springframework.http.ResponseEntity import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* @@ -12,6 +18,7 @@ import org.springframework.web.bind.annotation.* class ChatController( private val chatService: ChatService, val authClient: AuthClient, + val serverClient: ServerClient, ) { @PostMapping("/chat") fun createChat( @@ -23,14 +30,26 @@ class ChatController( return ResponseEntity.ok().body(result) } - @GetMapping("/chat/{chatRoomId}/{page}") - fun getChatsByChatRoom( - @PathVariable("chatRoomId") chatRoomId: String, - @PathVariable("page") page: Int, + @GetMapping("/chat") + fun getChats( + @RequestParam("type") type: String, + @RequestParam("id") id: String, + @RequestParam("page") page: Int, @RequestHeader("Authorization") token: String, ): ResponseEntity<*> { val userId = authClient.getTokenInfo(token).data!!.userId - val result = chatService.getChatsByChatRoom(chatRoomId, userId, page) - return ResponseEntity.ok().body(result) + val result = + when (type) { + ChatType.Room.toString() -> chatService.getRoomChats(id, userId, page) + ChatType.Server.toString() -> + chatService.getServerChats( + id, + userId, + page, + serverClient.getServerList(token, GetServerCondition()).body!!.data!!, + ) + else -> throw GlobalException(ErrorCode.INVALID_CHAT_TYPE) + } + return ResponseEntity.ok().body(ApiResponse(data = result, status = 200)) } } diff --git a/chat/src/main/kotlin/kpring/chat/chat/model/Chat.kt b/chat/src/main/kotlin/kpring/chat/chat/model/RoomChat.kt similarity index 96% rename from chat/src/main/kotlin/kpring/chat/chat/model/Chat.kt rename to chat/src/main/kotlin/kpring/chat/chat/model/RoomChat.kt index 30f2f80c..c11c2290 100644 --- a/chat/src/main/kotlin/kpring/chat/chat/model/Chat.kt +++ b/chat/src/main/kotlin/kpring/chat/chat/model/RoomChat.kt @@ -7,7 +7,7 @@ import org.springframework.data.mongodb.core.mapping.Document @NoArg @Document(collection = "chats") -class Chat( +class RoomChat( val userId: String, val roomId: String, val content: String, diff --git a/chat/src/main/kotlin/kpring/chat/chat/model/ServerChat.kt b/chat/src/main/kotlin/kpring/chat/chat/model/ServerChat.kt new file mode 100644 index 00000000..5eef055b --- /dev/null +++ b/chat/src/main/kotlin/kpring/chat/chat/model/ServerChat.kt @@ -0,0 +1,21 @@ +package kpring.chat.chat.model + +import kpring.chat.NoArg +import kpring.chat.global.model.BaseTime +import org.springframework.data.annotation.Id +import org.springframework.data.mongodb.core.mapping.Document + +@NoArg +@Document(collection = "server_chats") +class ServerChat( + val userId: String, + val serverId: String, + val content: String, +) : BaseTime() { + @Id + var id: String? = null + + fun isEdited(): Boolean { + return !createdAt.equals(updatedAt) + } +} diff --git a/chat/src/main/kotlin/kpring/chat/chat/repository/ChatRepository.kt b/chat/src/main/kotlin/kpring/chat/chat/repository/RoomChatRepository.kt similarity index 68% rename from chat/src/main/kotlin/kpring/chat/chat/repository/ChatRepository.kt rename to chat/src/main/kotlin/kpring/chat/chat/repository/RoomChatRepository.kt index e6413edb..f8506d1e 100644 --- a/chat/src/main/kotlin/kpring/chat/chat/repository/ChatRepository.kt +++ b/chat/src/main/kotlin/kpring/chat/chat/repository/RoomChatRepository.kt @@ -1,15 +1,15 @@ package kpring.chat.chat.repository -import kpring.chat.chat.model.Chat +import kpring.chat.chat.model.RoomChat import org.springframework.data.domain.Pageable import org.springframework.data.mongodb.repository.MongoRepository import org.springframework.data.querydsl.QuerydslPredicateExecutor import org.springframework.stereotype.Repository @Repository -interface ChatRepository : MongoRepository, QuerydslPredicateExecutor { +interface RoomChatRepository : MongoRepository, QuerydslPredicateExecutor { fun findAllByRoomId( roomId: String, pageable: Pageable, - ): List + ): List } diff --git a/chat/src/main/kotlin/kpring/chat/chat/repository/ServerChatRepository.kt b/chat/src/main/kotlin/kpring/chat/chat/repository/ServerChatRepository.kt new file mode 100644 index 00000000..7bc1ad42 --- /dev/null +++ b/chat/src/main/kotlin/kpring/chat/chat/repository/ServerChatRepository.kt @@ -0,0 +1,15 @@ +package kpring.chat.chat.repository + +import kpring.chat.chat.model.ServerChat +import org.springframework.data.domain.Pageable +import org.springframework.data.mongodb.repository.MongoRepository +import org.springframework.data.querydsl.QuerydslPredicateExecutor +import org.springframework.stereotype.Repository + +@Repository +interface ServerChatRepository : MongoRepository, QuerydslPredicateExecutor { + fun findAllByServerId( + serverId: String, + pageable: Pageable, + ): List +} diff --git a/chat/src/main/kotlin/kpring/chat/chat/service/ChatService.kt b/chat/src/main/kotlin/kpring/chat/chat/service/ChatService.kt index bb4e48d2..4e829977 100644 --- a/chat/src/main/kotlin/kpring/chat/chat/service/ChatService.kt +++ b/chat/src/main/kotlin/kpring/chat/chat/service/ChatService.kt @@ -1,12 +1,15 @@ package kpring.chat.chat.service -import kpring.chat.chat.model.Chat -import kpring.chat.chat.repository.ChatRepository +import kpring.chat.chat.model.RoomChat +import kpring.chat.chat.model.ServerChat +import kpring.chat.chat.repository.RoomChatRepository +import kpring.chat.chat.repository.ServerChatRepository import kpring.chat.chatroom.repository.ChatRoomRepository import kpring.chat.global.exception.ErrorCode import kpring.chat.global.exception.GlobalException import kpring.core.chat.chat.dto.request.CreateChatRequest import kpring.core.chat.chat.dto.response.ChatResponse +import kpring.core.server.dto.ServerSimpleInfo import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable @@ -14,20 +17,18 @@ import org.springframework.stereotype.Service @Service class ChatService( - private val chatRepository: ChatRepository, + private val roomChatRepository: RoomChatRepository, + private val serverChatRepository: ServerChatRepository, private val chatRoomRepository: ChatRoomRepository, @Value("\${page.size}") val pageSize: Int = 100, ) { - /* - business logic - */ fun createChat( request: CreateChatRequest, userId: String, ) { - val chat = - chatRepository.save( - Chat( + val roomChat = + roomChatRepository.save( + RoomChat( userId = userId, roomId = request.room, content = request.content, @@ -35,35 +36,67 @@ class ChatService( ) } - fun getChatsByChatRoom( + fun getRoomChats( chatRoomId: String, userId: String, page: Int, ): List { - checkIfAuthorized(chatRoomId, userId) + verifyChatRoomAccess(chatRoomId, userId) - // find chats by chatRoomId and convert them into DTOs val pageable: Pageable = PageRequest.of(page, pageSize) - val chats: List = chatRepository.findAllByRoomId(chatRoomId, pageable) + val roomChats: List = roomChatRepository.findAllByRoomId(chatRoomId, pageable) - return convertChatsToResponses(chats) + return convertRoomChatsToResponses(roomChats) } - fun checkIfAuthorized( + fun getServerChats( + serverId: String, + userId: String, + page: Int, + servers: List, + ): List { + verifyServerAccess(servers, serverId) + + val pageable: Pageable = PageRequest.of(page, pageSize) + val chats: List = serverChatRepository.findAllByServerId(serverId, pageable) + + return convertServerChatsToResponses(chats) + } + + private fun verifyServerAccess( + servers: List, + serverId: String, + ) { + servers.forEach { info -> + if (info.id.equals(serverId)) { + return + } + } + throw GlobalException(ErrorCode.FORBIDDEN_SERVER) + } + + private fun verifyChatRoomAccess( chatRoomId: String, userId: String, ) { - // check if there is a chatroom with the chatRoomId and the user is one of the members if (!chatRoomRepository.existsByIdAndMembersContaining(chatRoomId, userId)) { - throw GlobalException(ErrorCode.UNAUTHORIZED_CHATROOM) + throw GlobalException(ErrorCode.FORBIDDEN_CHATROOM) } } - fun convertChatsToResponses(chats: List): List { - val chatResponses = + private fun convertRoomChatsToResponses(roomChats: List): List { + val chatResponse = + roomChats.map { chat -> + ChatResponse(chat.id!!, chat.isEdited(), chat.createdAt.toString(), chat.content) + } + return chatResponse + } + + private fun convertServerChatsToResponses(chats: List): List { + val chatResponse = chats.map { chat -> - ChatResponse(chat.roomId, chat.isEdited(), chat.createdAt, chat.content) + ChatResponse(chat.id!!, chat.isEdited(), chat.createdAt.toString(), chat.content) } - return chatResponses + return chatResponse } } diff --git a/chat/src/main/kotlin/kpring/chat/chatroom/service/ChatRoomService.kt b/chat/src/main/kotlin/kpring/chat/chatroom/service/ChatRoomService.kt index bbb7a676..f277d55f 100644 --- a/chat/src/main/kotlin/kpring/chat/chatroom/service/ChatRoomService.kt +++ b/chat/src/main/kotlin/kpring/chat/chatroom/service/ChatRoomService.kt @@ -24,19 +24,18 @@ class ChatRoomService( chatRoomId: String, userId: String, ) { - verifyAuthorizationForChatRoom(chatRoomId, userId) + verifyChatRoomAccess(chatRoomId, userId) val chatRoom: ChatRoom = getChatRoom(chatRoomId) chatRoom.removeUser(userId) chatRoomRepository.save(chatRoom) } - fun verifyAuthorizationForChatRoom( + fun verifyChatRoomAccess( chatRoomId: String, userId: String, ) { - // check if there is a chatroom with the chatRoomId and the user is one of the members if (!chatRoomRepository.existsByIdAndMembersContaining(chatRoomId, userId)) { - throw GlobalException(ErrorCode.UNAUTHORIZED_CHATROOM) + throw GlobalException(ErrorCode.FORBIDDEN_CHATROOM) } } diff --git a/chat/src/main/kotlin/kpring/chat/global/config/AuthConfig.kt b/chat/src/main/kotlin/kpring/chat/global/config/AuthConfig.kt new file mode 100644 index 00000000..8c7eeeeb --- /dev/null +++ b/chat/src/main/kotlin/kpring/chat/global/config/AuthConfig.kt @@ -0,0 +1,9 @@ +package kpring.chat.global.config + +import kpring.core.auth.config.AuthClientConfig +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import + +@Import(AuthClientConfig::class) +@Configuration +class AuthConfig diff --git a/chat/src/main/kotlin/kpring/chat/global/config/ClientConfig.kt b/chat/src/main/kotlin/kpring/chat/global/config/ClientConfig.kt deleted file mode 100644 index 3e7ebe65..00000000 --- a/chat/src/main/kotlin/kpring/chat/global/config/ClientConfig.kt +++ /dev/null @@ -1,26 +0,0 @@ -package kpring.chat.global.config - -import kpring.core.auth.client.AuthClient -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.web.client.RestClient -import org.springframework.web.client.support.RestClientAdapter -import org.springframework.web.service.invoker.HttpServiceProxyFactory - -@Configuration -class ClientConfig { - @Value("\${auth.url}") - private val authUrl: String? = null - - @Bean - fun authClient(): AuthClient { - val restClient = - RestClient.builder() - .baseUrl(authUrl!!) - .build() - val adapter = RestClientAdapter.create(restClient) - val factory = HttpServiceProxyFactory.builderFor(adapter).build() - return factory.createClient(AuthClient::class.java) - } -} diff --git a/chat/src/main/kotlin/kpring/chat/global/config/ServerConfig.kt b/chat/src/main/kotlin/kpring/chat/global/config/ServerConfig.kt new file mode 100644 index 00000000..fbd39294 --- /dev/null +++ b/chat/src/main/kotlin/kpring/chat/global/config/ServerConfig.kt @@ -0,0 +1,9 @@ +package kpring.chat.global.config + +import kpring.core.server.config.ServerClientConfig +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import + +@Import(ServerClientConfig::class) +@Configuration +class ServerConfig diff --git a/chat/src/main/kotlin/kpring/chat/global/exception/ErrorCode.kt b/chat/src/main/kotlin/kpring/chat/global/exception/ErrorCode.kt index e43aed4f..09798d7a 100644 --- a/chat/src/main/kotlin/kpring/chat/global/exception/ErrorCode.kt +++ b/chat/src/main/kotlin/kpring/chat/global/exception/ErrorCode.kt @@ -4,13 +4,12 @@ import org.springframework.http.HttpStatus enum class ErrorCode(val httpStatus: Int, val message: String) { // 400 - INVALID_TOKEN(HttpStatus.BAD_REQUEST.value(), "토큰이 유효하지 않습니다"), + INVALID_CHAT_TYPE(HttpStatus.BAD_REQUEST.value(), "잘못된 ChatType입니다."), - // 401 - UNAUTHORIZED_CHATROOM(HttpStatus.UNAUTHORIZED.value(), "접근이 제한된 채팅방 입니다"), + // 403 + FORBIDDEN_CHATROOM(HttpStatus.FORBIDDEN.value(), "접근이 제한된 채팅방 입니다"), + FORBIDDEN_SERVER(HttpStatus.FORBIDDEN.value(), "접근이 제한된 서버 입니다"), // 404 - INVALID_TOKEN_BODY(HttpStatus.NOT_FOUND.value(), "토큰의 body가 유효하지 않습니다"), - USERID_NOT_EXIST(HttpStatus.NOT_FOUND.value(), "토큰의 userId가 존재하지 않습니다"), CHATROOM_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 id로 chatroom을 찾을 수 없습니다"), } diff --git a/chat/src/main/resources/application.yml b/chat/src/main/resources/application.yml index 6acdae2b..0590f9e8 100644 --- a/chat/src/main/resources/application.yml +++ b/chat/src/main/resources/application.yml @@ -15,6 +15,8 @@ server: port: 8081 auth: - url: "http://localhost:8080/" + url: "http://localhost:30000/" +url: + server: "http://localhost:8080/" page: size: 100 diff --git a/chat/src/test/kotlin/kpring/chat/chat/ChatServiceTest.kt b/chat/src/test/kotlin/kpring/chat/chat/ChatServiceTest.kt index 3adc9278..0a625181 100644 --- a/chat/src/test/kotlin/kpring/chat/chat/ChatServiceTest.kt +++ b/chat/src/test/kotlin/kpring/chat/chat/ChatServiceTest.kt @@ -2,13 +2,13 @@ package kpring.chat.chat import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify -import kpring.chat.chat.model.Chat -import kpring.chat.chat.repository.ChatRepository +import kpring.chat.chat.model.RoomChat +import kpring.chat.chat.repository.RoomChatRepository +import kpring.chat.chat.repository.ServerChatRepository import kpring.chat.chat.service.ChatService import kpring.chat.chatroom.repository.ChatRoomRepository import kpring.chat.global.ChatRoomTest @@ -17,32 +17,32 @@ import kpring.chat.global.CommonTest import kpring.chat.global.exception.ErrorCode import kpring.chat.global.exception.GlobalException import kpring.core.chat.chat.dto.request.CreateChatRequest -import kpring.core.chat.chat.dto.response.ChatResponse +import kpring.core.server.dto.ServerSimpleInfo import org.springframework.beans.factory.annotation.Value -import org.springframework.data.domain.PageRequest class ChatServiceTest( @Value("\${page.size}") val pageSize: Int = 100, ) : FunSpec({ - val chatRepository = mockk() + val roomChatRepository = mockk() + val serverChatRepository = mockk() val chatRoomRepository = mockk() - val chatService = ChatService(chatRepository, chatRoomRepository) + val chatService = ChatService(roomChatRepository, serverChatRepository, chatRoomRepository) - test("createChat 은 새 Chat을 저장해야 한다") { + test("createChat 은 새 RoomChat을 저장해야 한다") { // Given val request = CreateChatRequest(ChatRoomTest.TEST_ROOM_ID, ChatTest.CONTENT) val userId = CommonTest.TEST_USER_ID - val chat = Chat(userId, request.room, request.content) - every { chatRepository.save(any()) } returns chat + val roomChat = RoomChat(userId, request.room, request.content) + every { roomChatRepository.save(any()) } returns roomChat // When chatService.createChat(request, userId) // Then - verify { chatRepository.save(any()) } + verify { roomChatRepository.save(any()) } } - test("getChatsByChatRoom 은 권한이 없는 사용자에게 에러 발생") { + test("getRoomChats 은 권한이 없는 사용자에게 에러 발생") { // Given val chatRoomId = ChatRoomTest.TEST_ROOM_ID val userId = CommonTest.TEST_ANOTHER_USER_ID @@ -51,32 +51,37 @@ class ChatServiceTest( // When & Then val exception = shouldThrow { - chatService.getChatsByChatRoom(chatRoomId, userId, 1) + chatService.getRoomChats(chatRoomId, userId, 1) } val errorCodeField = GlobalException::class.java.getDeclaredField("errorCode") errorCodeField.isAccessible = true val errorCode = errorCodeField.get(exception) as ErrorCode - errorCode shouldBe ErrorCode.UNAUTHORIZED_CHATROOM + errorCode shouldBe ErrorCode.FORBIDDEN_CHATROOM } - test("getChatsByChatRoom 은 권한이 있는 사용자에게 Chat들을 반환") { + test("getServerChats 은 권한이 없는 사용자에게 에러 발생") { // Given - val chatRoomId = ChatRoomTest.TEST_ROOM_ID - val userId = CommonTest.TEST_USER_ID - val chat1 = Chat(userId, chatRoomId, "Message 1") - val chat2 = Chat(userId, chatRoomId, "Message 2") - every { chatRepository.findAllByRoomId(chatRoomId, pageable = PageRequest.of(1, pageSize)) } returns + val serverId1 = "test_server_id" + val serverId2 = "test_another_server_id" + val userId = CommonTest.TEST_ANOTHER_USER_ID + + val serverList = listOf( - chat1, chat2, + ServerSimpleInfo( + id = serverId2, + name = "test_server_name", + bookmarked = true, + ), ) - every { chatRoomRepository.existsByIdAndMembersContaining(chatRoomId, userId) } returns true - // When - val chatResponses = chatService.getChatsByChatRoom(chatRoomId, userId, 1) - - // Then - chatResponses.size shouldBe 2 - chatResponses.shouldContain(ChatResponse(chatRoomId, false, chat1.createdAt, "Message 1")) - chatResponses.shouldContain(ChatResponse(chatRoomId, false, chat2.createdAt, "Message 2")) + // When & Then + val exception = + shouldThrow { + chatService.getServerChats(serverId1, userId, 1, serverList) + } + val errorCodeField = GlobalException::class.java.getDeclaredField("errorCode") + errorCodeField.isAccessible = true + val errorCode = errorCodeField.get(exception) as ErrorCode + errorCode shouldBe ErrorCode.FORBIDDEN_SERVER } }) diff --git a/chat/src/test/kotlin/kpring/chat/chat/api/v1/ChatControllerTest.kt b/chat/src/test/kotlin/kpring/chat/chat/api/v1/ChatControllerTest.kt new file mode 100644 index 00000000..c0c35467 --- /dev/null +++ b/chat/src/test/kotlin/kpring/chat/chat/api/v1/ChatControllerTest.kt @@ -0,0 +1,228 @@ +package kpring.chat.chat.api.v1 + +import com.fasterxml.jackson.databind.ObjectMapper +import com.ninjasquad.springmockk.MockkBean +import io.kotest.core.spec.style.DescribeSpec +import io.mockk.every +import io.mockk.junit5.MockKExtension +import kpring.chat.chat.service.ChatService +import kpring.chat.global.CommonTest +import kpring.core.auth.client.AuthClient +import kpring.core.auth.dto.response.TokenInfo +import kpring.core.auth.enums.TokenType +import kpring.core.chat.chat.dto.response.ChatResponse +import kpring.core.global.dto.response.ApiResponse +import kpring.core.server.client.ServerClient +import kpring.core.server.dto.ServerSimpleInfo +import kpring.test.restdoc.dsl.restDoc +import kpring.test.restdoc.json.JsonDataType +import kpring.test.web.URLBuilder +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.http.ResponseEntity +import org.springframework.restdocs.ManualRestDocumentation +import org.springframework.restdocs.RestDocumentationExtension +import org.springframework.restdocs.operation.preprocess.Preprocessors +import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation +import org.springframework.test.context.junit.jupiter.SpringExtension +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.test.web.servlet.client.MockMvcWebTestClient +import org.springframework.web.context.WebApplicationContext +import java.time.LocalDateTime + +@WebMvcTest(controllers = [ChatController::class]) +@ExtendWith(RestDocumentationExtension::class) +@ExtendWith(SpringExtension::class) +@ExtendWith(MockKExtension::class) +class ChatControllerTest( + private val om: ObjectMapper, + webContext: WebApplicationContext, + @MockkBean val chatService: ChatService, + @MockkBean val serverClient: ServerClient, + @MockkBean val authClient: AuthClient, +) : DescribeSpec({ + + val restDocument = ManualRestDocumentation() + val webTestClient: WebTestClient = + MockMvcWebTestClient.bindToApplicationContext(webContext).configureClient() + .baseUrl("http://localhost:8081") + .filter( + WebTestClientRestDocumentation.documentationConfiguration(restDocument).operationPreprocessors() + .withRequestDefaults(Preprocessors.prettyPrint()).withResponseDefaults(Preprocessors.prettyPrint()), + ) + .build() + + beforeSpec { restDocument.beforeTest(this.javaClass, "chat controller") } + + afterSpec { restDocument.afterTest() } + + describe("GET /api/v1/chat : getServerChats api test") { + + val url = "/api/v1/chat" + it("getServerChats api test") { + + // Given + val serverId = "test_server_id" + val data = + listOf( + ChatResponse( + serverId, + false, + LocalDateTime.now().toString(), + "sad", + ), + ) + val serverList = + listOf( + ServerSimpleInfo( + id = serverId, + name = "test_server_name", + bookmarked = true, + ), + ) + + every { authClient.getTokenInfo(any()) } returns + ApiResponse( + data = + TokenInfo( + type = TokenType.ACCESS, userId = CommonTest.TEST_USER_ID, + ), + ) + + every { serverClient.getServerList(any(), any()) } returns + ResponseEntity.ok().body(ApiResponse(data = serverList)) + + every { + chatService.getServerChats( + serverId, + CommonTest.TEST_USER_ID, + 1, + serverList, + ) + } returns data + + // When + val result = + webTestClient.get().uri( + URLBuilder(url) + .query("id", serverId) + .query("type", "Server") + .query("page", 1) + .build(), + ) + .header("Authorization", "Bearer mock_token") + .exchange() + + val docs = + result + .expectStatus() + .isOk + .expectBody() + .json(om.writeValueAsString(ApiResponse(data = data))) + + // Then + docs.restDoc( + identifier = "get_server_chats_200", + description = "서버 채팅 조회 api", + ) { + + request { + query { + + "type" mean "Server / Room" + "id" mean "서버 ID" + "page" mean "페이지 번호" + } + } + + response { + body { + "status" type JsonDataType.Integers mean "상태 코드" + "data[].id" type JsonDataType.Strings mean "서버 ID" + "data[].isEdited" type JsonDataType.Booleans mean "메시지가 수정되었는지 여부" + "data[].sentAt" type JsonDataType.Strings mean "메시지 생성 시간" + "data[].content" type JsonDataType.Strings mean "메시지 내용" + } + } + } + } + } + + describe("GET /api/v1/chat : getRoomChats api test") { + + val url = "/api/v1/chat" + it("getRoomChats api test") { + + // Given + val roomId = "test_room_id" + val data = + listOf( + ChatResponse( + roomId, + false, + LocalDateTime.now().toString(), + "sad", + ), + ) + + every { authClient.getTokenInfo(any()) } returns + ApiResponse( + data = + TokenInfo( + type = TokenType.ACCESS, userId = CommonTest.TEST_USER_ID, + ), + ) + + every { + chatService.getRoomChats( + roomId, + CommonTest.TEST_USER_ID, + 1, + ) + } returns data + + // When + val result = + webTestClient.get().uri( + URLBuilder(url) + .query("id", roomId) + .query("type", "Room") + .query("page", 1) + .build(), + ) + .header("Authorization", "Bearer mock_token") + .exchange() + + val docs = + result + .expectStatus() + .isOk + .expectBody() + .json(om.writeValueAsString(ApiResponse(data = data))) + + // Then + docs.restDoc( + identifier = "get_room_chats_200", + description = "채팅방 채팅 조회 api", + ) { + request { + query { + "type" mean "Server / Room" + "id" mean "채팅방 ID" + "page" mean "페이지 번호" + } + } + + response { + body { + "status" type JsonDataType.Integers mean "상태 코드" + "data[].id" type JsonDataType.Strings mean "채팅방 ID" + "data[].isEdited" type JsonDataType.Booleans mean "메시지가 수정되었는지 여부" + "data[].sentAt" type JsonDataType.Strings mean "메시지 생성 시간" + "data[].content" type JsonDataType.Strings mean "메시지 내용" + } + } + } + } + } + }) diff --git a/chat/src/test/kotlin/kpring/chat/example/SampleTest.kt b/chat/src/test/kotlin/kpring/chat/example/SampleTest.kt index 9c4a9b97..2fa7d1f7 100644 --- a/chat/src/test/kotlin/kpring/chat/example/SampleTest.kt +++ b/chat/src/test/kotlin/kpring/chat/example/SampleTest.kt @@ -3,9 +3,9 @@ package kpring.chat.example import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import kpring.chat.chat.model.Chat -import kpring.chat.chat.model.QChat -import kpring.chat.chat.repository.ChatRepository +import kpring.chat.chat.model.QRoomChat +import kpring.chat.chat.model.RoomChat +import kpring.chat.chat.repository.RoomChatRepository import kpring.test.testcontainer.SpringTestContext import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ContextConfiguration @@ -16,25 +16,25 @@ import org.springframework.test.context.ContextConfiguration @SpringBootTest @ContextConfiguration(initializers = [SpringTestContext.SpringDataMongo::class]) class SampleTest( - val chatRepository: ChatRepository, + val roomChatRepository: RoomChatRepository, ) : DescribeSpec({ beforeTest { - chatRepository.deleteAll() + roomChatRepository.deleteAll() } it("query dsl 적용 테스트") { // given - val chat = QChat.chat + val chat = QRoomChat.roomChat repeat(5) { idx -> - chatRepository.save( - Chat("testUserId", "testRoomId", "testContent$idx"), + roomChatRepository.save( + RoomChat("testUserId", "testRoomId", "testContent$idx"), ) } // when val result = - chatRepository.findAll( + roomChatRepository.findAll( chat.userId.eq("testUserId"), chat.userId.asc(), ) @@ -49,17 +49,17 @@ class SampleTest( it("query dsl 적용 테스트 : 다중 조건") { // given - val chat = QChat.chat - chatRepository.deleteAll() + val chat = QRoomChat.roomChat + roomChatRepository.deleteAll() repeat(5) { idx -> - chatRepository.save( - Chat("testUserId", "testRoomId", "testContent$idx"), + roomChatRepository.save( + RoomChat("testUserId", "testRoomId", "testContent$idx"), ) } // when val result = - chatRepository.findAll( + roomChatRepository.findAll( chat.userId.eq("testUserId") .and(chat.content.contains("testContent")) // null을 적용하면 조건이 적용되지 않는다. diff --git a/core/src/main/kotlin/kpring/core/chat/chat/dto/request/ChatType.kt b/core/src/main/kotlin/kpring/core/chat/chat/dto/request/ChatType.kt new file mode 100644 index 00000000..8093852f --- /dev/null +++ b/core/src/main/kotlin/kpring/core/chat/chat/dto/request/ChatType.kt @@ -0,0 +1,10 @@ +package kpring.core.chat.chat.dto.request + +enum class ChatType(val type: String) { + Room("Room"), + Server("Server"); + + override fun toString(): String{ + return type; + } +} diff --git a/core/src/main/kotlin/kpring/core/chat/chat/dto/response/ChatResponse.kt b/core/src/main/kotlin/kpring/core/chat/chat/dto/response/ChatResponse.kt index 27d69386..5d872853 100644 --- a/core/src/main/kotlin/kpring/core/chat/chat/dto/response/ChatResponse.kt +++ b/core/src/main/kotlin/kpring/core/chat/chat/dto/response/ChatResponse.kt @@ -1,10 +1,8 @@ package kpring.core.chat.chat.dto.response -import java.time.LocalDateTime - data class ChatResponse( val id: String, val isEdited: Boolean, - val sentAt: LocalDateTime, + val sentAt: String, val content: String, ) diff --git a/core/src/main/kotlin/kpring/core/server/client/ServerClient.kt b/core/src/main/kotlin/kpring/core/server/client/ServerClient.kt new file mode 100644 index 00000000..1af84ac7 --- /dev/null +++ b/core/src/main/kotlin/kpring/core/server/client/ServerClient.kt @@ -0,0 +1,17 @@ +package kpring.core.server.client + +import kpring.core.global.dto.response.ApiResponse +import kpring.core.server.dto.ServerSimpleInfo +import kpring.core.server.dto.request.GetServerCondition +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.RequestHeader +import org.springframework.web.service.annotation.GetExchange + +interface ServerClient { + @GetExchange("/api/v1/server") + fun getServerList( + @RequestHeader("Authorization") token: String, + @ModelAttribute condition: GetServerCondition, + ): ResponseEntity>> +} diff --git a/core/src/main/kotlin/kpring/core/server/config/ServerClientConfig.kt b/core/src/main/kotlin/kpring/core/server/config/ServerClientConfig.kt new file mode 100644 index 00000000..86da2da4 --- /dev/null +++ b/core/src/main/kotlin/kpring/core/server/config/ServerClientConfig.kt @@ -0,0 +1,48 @@ +package kpring.core.server.config + +import kpring.core.global.exception.HttpClientErrorHandle.rest4xxHandle +import kpring.core.global.exception.HttpClientErrorHandle.rest5xxHandle +import kpring.core.server.client.ServerClient +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.HttpStatusCode +import org.springframework.http.client.JdkClientHttpRequestFactory +import org.springframework.web.client.RestClient +import org.springframework.web.client.support.RestClientAdapter +import org.springframework.web.service.invoker.HttpServiceProxyFactory +import java.net.http.HttpClient +import java.time.Duration + +@Configuration +class ServerClientConfig { + private val valField: String? = null + + @Value("\${url.server}") + private lateinit var serverUrl: String + + @Value("\${server.read-timeout:1s}") + private lateinit var readTimeout: Duration + + @Value("\${server.connect-timeout:5s}") + private lateinit var connectTimeout: Duration + + @Bean + fun serverClient(): ServerClient { + println(valField) + val client = HttpClient.newBuilder().connectTimeout(connectTimeout).version(HttpClient.Version.HTTP_2).build() + + val fac = JdkClientHttpRequestFactory(client) + fac.setReadTimeout(readTimeout) + + val restClient = + RestClient.builder().requestFactory(fac).baseUrl(serverUrl) + .defaultStatusHandler(HttpStatusCode::is4xxClientError, rest4xxHandle()) + .defaultStatusHandler(HttpStatusCode::is5xxServerError, rest5xxHandle()).build() + + val adapter = RestClientAdapter.create(restClient) + val factory = HttpServiceProxyFactory.builderFor(adapter).build() + + return factory.createClient(ServerClient::class.java) + } +}