From beb6774ea9f6fe8591212a4c26116d6ce500bddb Mon Sep 17 00:00:00 2001 From: ahnyunki Date: Sun, 24 Mar 2024 14:58:51 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20TalkRoom=20Comment=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/comment/CommentController.java | 31 +++++++++++++++ .../comment/request/CommentCreateRequest.java | 27 +++++++++++++ .../application/comment/CommentService.java | 38 +++++++++++++++++++ .../request/CommentCreateServiceRequest.java | 18 +++++++++ .../comment/response/CommentResponse.java | 28 ++++++++++++++ .../com/jisungin/domain/comment/Comment.java | 9 +++++ .../comment/repository/CommentRepository.java | 10 +++++ 7 files changed, 161 insertions(+) create mode 100644 src/main/java/com/jisungin/api/comment/CommentController.java create mode 100644 src/main/java/com/jisungin/api/comment/request/CommentCreateRequest.java create mode 100644 src/main/java/com/jisungin/application/comment/CommentService.java create mode 100644 src/main/java/com/jisungin/application/comment/request/CommentCreateServiceRequest.java create mode 100644 src/main/java/com/jisungin/application/comment/response/CommentResponse.java create mode 100644 src/main/java/com/jisungin/domain/comment/repository/CommentRepository.java diff --git a/src/main/java/com/jisungin/api/comment/CommentController.java b/src/main/java/com/jisungin/api/comment/CommentController.java new file mode 100644 index 0000000..39f8e67 --- /dev/null +++ b/src/main/java/com/jisungin/api/comment/CommentController.java @@ -0,0 +1,31 @@ +package com.jisungin.api.comment; + +import com.jisungin.api.ApiResponse; +import com.jisungin.api.comment.request.CommentCreateRequest; +import com.jisungin.api.oauth.Auth; +import com.jisungin.api.oauth.AuthContext; +import com.jisungin.application.comment.CommentService; +import com.jisungin.application.comment.response.CommentResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RequestMapping("/v1") +@RestController +public class CommentController { + + private final CommentService commentService; + + @PostMapping("/talk-rooms/{talkRoomId}/comments") + public ApiResponse writeComment(@PathVariable Long talkRoomId, + @Valid @RequestBody CommentCreateRequest request, + @Auth AuthContext authContext) { + return ApiResponse.ok(commentService.writeComment(request.toService(), talkRoomId, authContext.getUserId())); + } + +} diff --git a/src/main/java/com/jisungin/api/comment/request/CommentCreateRequest.java b/src/main/java/com/jisungin/api/comment/request/CommentCreateRequest.java new file mode 100644 index 0000000..234a52e --- /dev/null +++ b/src/main/java/com/jisungin/api/comment/request/CommentCreateRequest.java @@ -0,0 +1,27 @@ +package com.jisungin.api.comment.request; + +import com.jisungin.application.comment.request.CommentCreateServiceRequest; +import jakarta.validation.constraints.NotBlank; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class CommentCreateRequest { + + @NotBlank(message = "내용은 필수 입니다.") + private String content; + + @Builder + private CommentCreateRequest(String content) { + this.content = content; + } + + public CommentCreateServiceRequest toService() { + return CommentCreateServiceRequest.builder() + .content(content) + .build(); + } + +} diff --git a/src/main/java/com/jisungin/application/comment/CommentService.java b/src/main/java/com/jisungin/application/comment/CommentService.java new file mode 100644 index 0000000..cb4e1b8 --- /dev/null +++ b/src/main/java/com/jisungin/application/comment/CommentService.java @@ -0,0 +1,38 @@ +package com.jisungin.application.comment; + +import com.jisungin.application.comment.request.CommentCreateServiceRequest; +import com.jisungin.application.comment.response.CommentResponse; +import com.jisungin.domain.comment.Comment; +import com.jisungin.domain.comment.repository.CommentRepository; +import com.jisungin.domain.talkroom.TalkRoom; +import com.jisungin.domain.talkroom.repository.TalkRoomRepository; +import com.jisungin.domain.user.User; +import com.jisungin.domain.user.repository.UserRepository; +import com.jisungin.exception.BusinessException; +import com.jisungin.exception.ErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CommentService { + + private final CommentRepository commentRepository; + private final TalkRoomRepository talkRoomRepository; + private final UserRepository userRepository; + + public CommentResponse writeComment(CommentCreateServiceRequest request, Long talkRoomId, Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); + + TalkRoom talkRoom = talkRoomRepository.findById(talkRoomId) + .orElseThrow(() -> new BusinessException(ErrorCode.TALK_ROOM_NOT_FOUND)); + + Comment comment = Comment.create(request, user, talkRoom); + + commentRepository.save(comment); + + return CommentResponse.of(comment.getContent(), user.getName()); + } + +} diff --git a/src/main/java/com/jisungin/application/comment/request/CommentCreateServiceRequest.java b/src/main/java/com/jisungin/application/comment/request/CommentCreateServiceRequest.java new file mode 100644 index 0000000..7ed4561 --- /dev/null +++ b/src/main/java/com/jisungin/application/comment/request/CommentCreateServiceRequest.java @@ -0,0 +1,18 @@ +package com.jisungin.application.comment.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class CommentCreateServiceRequest { + + private String content; + + @Builder + private CommentCreateServiceRequest(String content) { + this.content = content; + } + +} diff --git a/src/main/java/com/jisungin/application/comment/response/CommentResponse.java b/src/main/java/com/jisungin/application/comment/response/CommentResponse.java new file mode 100644 index 0000000..68627cd --- /dev/null +++ b/src/main/java/com/jisungin/application/comment/response/CommentResponse.java @@ -0,0 +1,28 @@ +package com.jisungin.application.comment.response; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class CommentResponse { + + private String content; + + private String userName; + + @Builder + private CommentResponse(String content, String userName) { + this.content = content; + this.userName = userName; + } + + public static CommentResponse of(String content, String name) { + return CommentResponse.builder() + .content(content) + .userName(name) + .build(); + } + +} diff --git a/src/main/java/com/jisungin/domain/comment/Comment.java b/src/main/java/com/jisungin/domain/comment/Comment.java index b8681d4..54b3a70 100644 --- a/src/main/java/com/jisungin/domain/comment/Comment.java +++ b/src/main/java/com/jisungin/domain/comment/Comment.java @@ -1,5 +1,6 @@ package com.jisungin.domain.comment; +import com.jisungin.application.comment.request.CommentCreateServiceRequest; import com.jisungin.domain.BaseEntity; import com.jisungin.domain.talkroom.TalkRoom; import com.jisungin.domain.user.User; @@ -38,4 +39,12 @@ private Comment(User user, TalkRoom talkRoom, String content) { this.content = content; } + public static Comment create(CommentCreateServiceRequest request, User user, TalkRoom talkRoom) { + return Comment.builder() + .content(request.getContent()) + .user(user) + .talkRoom(talkRoom) + .build(); + } + } diff --git a/src/main/java/com/jisungin/domain/comment/repository/CommentRepository.java b/src/main/java/com/jisungin/domain/comment/repository/CommentRepository.java new file mode 100644 index 0000000..eec459d --- /dev/null +++ b/src/main/java/com/jisungin/domain/comment/repository/CommentRepository.java @@ -0,0 +1,10 @@ +package com.jisungin.domain.comment.repository; + +import com.jisungin.domain.comment.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentRepository extends JpaRepository { + +} From b18c88f8976dcf217be616b99bd27d530443c72f Mon Sep 17 00:00:00 2001 From: ahnyunki Date: Sun, 24 Mar 2024 14:59:18 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20TalkRoom=20Comment=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/comment/CommentControllerTest.java | 71 ++++++++++ .../comment/CommentServiceTest.java | 130 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 src/test/java/com/jisungin/api/comment/CommentControllerTest.java create mode 100644 src/test/java/com/jisungin/application/comment/CommentServiceTest.java diff --git a/src/test/java/com/jisungin/api/comment/CommentControllerTest.java b/src/test/java/com/jisungin/api/comment/CommentControllerTest.java new file mode 100644 index 0000000..8eb1a31 --- /dev/null +++ b/src/test/java/com/jisungin/api/comment/CommentControllerTest.java @@ -0,0 +1,71 @@ +package com.jisungin.api.comment; + +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +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 com.jisungin.api.comment.request.CommentCreateRequest; +import com.jisungin.application.comment.CommentService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +class CommentControllerTest { + + @MockBean + CommentService commentService; + + @Autowired + MockMvc mockMvc; + + @Autowired + ObjectMapper objectMapper; + + @Test + @DisplayName("유저가 토크방에 자신의 의견을 작성한다.") + void writeComment() throws Exception { + // given + CommentCreateRequest request = CommentCreateRequest.builder() + .content("의견을 작성하다") + .build(); + + // when // then + mockMvc.perform(post("/v1/talk-rooms/1/comments") + .content(objectMapper.writeValueAsString(request)) + .contentType(APPLICATION_JSON) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value("200")) + .andExpect(jsonPath("$.status").value("OK")) + .andExpect(jsonPath("$.message").value("OK")); + } + + @Test + @DisplayName("유저가 토크방에 자신의 의견을 남길 때 내용은 필수여야 한다.") + void writeCommentWithEmptyContent() throws Exception { + // given + CommentCreateRequest request = CommentCreateRequest.builder() + .build(); + + // when // then + mockMvc.perform(post("/v1/talk-rooms/1/comments") + .content(objectMapper.writeValueAsString(request)) + .contentType(APPLICATION_JSON) + ) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("400")) + .andExpect(jsonPath("$.message").value("내용은 필수 입니다.")); + } + +} \ No newline at end of file diff --git a/src/test/java/com/jisungin/application/comment/CommentServiceTest.java b/src/test/java/com/jisungin/application/comment/CommentServiceTest.java new file mode 100644 index 0000000..410a27f --- /dev/null +++ b/src/test/java/com/jisungin/application/comment/CommentServiceTest.java @@ -0,0 +1,130 @@ +package com.jisungin.application.comment; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.jisungin.application.comment.request.CommentCreateServiceRequest; +import com.jisungin.application.comment.response.CommentResponse; +import com.jisungin.domain.ReadingStatus; +import com.jisungin.domain.book.Book; +import com.jisungin.domain.book.repository.BookRepository; +import com.jisungin.domain.comment.repository.CommentRepository; +import com.jisungin.domain.oauth.OauthId; +import com.jisungin.domain.oauth.OauthType; +import com.jisungin.domain.talkroom.TalkRoom; +import com.jisungin.domain.talkroom.TalkRoomRole; +import com.jisungin.domain.talkroom.repository.TalkRoomRepository; +import com.jisungin.domain.talkroom.repository.TalkRoomRoleRepository; +import com.jisungin.domain.user.User; +import com.jisungin.domain.user.repository.UserRepository; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class CommentServiceTest { + + @Autowired + TalkRoomRepository talkRoomRepository; + + @Autowired + TalkRoomRoleRepository talkRoomRoleRepository; + + @Autowired + CommentService commentService; + + @Autowired + BookRepository bookRepository; + + @Autowired + UserRepository userRepository; + + @Autowired + CommentRepository commentRepository; + + @AfterEach + void tearDown() { + commentRepository.deleteAllInBatch(); + talkRoomRoleRepository.deleteAllInBatch(); + talkRoomRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + bookRepository.deleteAllInBatch(); + } + + @Test + @DisplayName("유저가 1번 토크방에 자신의 의견 작성한다.") + void writeComment() { + // given + User user = createUser(); + userRepository.save(user); + + Book book = createBook(); + bookRepository.save(book); + + TalkRoom talkRoom = createTalkRoom(book, user); + talkRoomRepository.save(talkRoom); + + createTalkRoomRole(talkRoom); + + CommentCreateServiceRequest request = CommentCreateServiceRequest.builder() + .content("의견 남기기") + .build(); + // when + CommentResponse response = commentService.writeComment(request, talkRoom.getId(), user.getId()); + + // then + assertThat(response) + .extracting("content", "userName") + .contains("의견 남기기", "user@gmail.com"); + } + + private void createTalkRoomRole(TalkRoom talkRoom) { + List request = new ArrayList<>(); + request.add("읽는 중"); + request.add("읽음"); + + List readingStatus = ReadingStatus.createReadingStatus(request); + + readingStatus.stream().map(status -> TalkRoomRole.roleCreate(talkRoom, status)) + .forEach(talkRoomRoleRepository::save); + } + + private static TalkRoom createTalkRoom(Book book, User user) { + return TalkRoom.builder() + .book(book) + .title("토크방") + .content("내용") + .user(user) + .build(); + } + + private static User createUser() { + return User.builder() + .name("user@gmail.com") + .profileImage("image") + .oauthId( + OauthId.builder() + .oauthId("oauthId") + .oauthType(OauthType.KAKAO) + .build() + ) + .build(); + } + + private static Book createBook() { + return Book.builder() + .title("제목") + .content("내용") + .authors("작가") + .isbn("11111") + .publisher("publisher") + .dateTime(LocalDateTime.now()) + .imageUrl("www") + .build(); + } + +} \ No newline at end of file