-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from RossKWSang/feature/findAllAnswer
[feat] 단일 질문에 대한 복수 답변 조회 기능
- Loading branch information
Showing
10 changed files
with
407 additions
and
11 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
src/main/java/com/kernel360/kernelsquare/domain/answer/controller/AnswerController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.kernel360.kernelsquare.domain.answer.controller; | ||
|
||
import com.kernel360.kernelsquare.domain.answer.dto.FindAnswerResponse; | ||
import com.kernel360.kernelsquare.domain.answer.service.AnswerService; | ||
import com.kernel360.kernelsquare.global.common_response.ApiResponse; | ||
import com.kernel360.kernelsquare.global.common_response.ResponseEntityFactory; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
import static com.kernel360.kernelsquare.global.common_response.response.code.AnswerResponseCode.ANSWERS_ALL_FOUND; | ||
|
||
@RestController | ||
@RequestMapping("/api/v1") | ||
@RequiredArgsConstructor | ||
public class AnswerController { | ||
|
||
private final AnswerService answerService; | ||
|
||
@GetMapping("/questions/{questionId}/answers") | ||
public ResponseEntity<ApiResponse<List<FindAnswerResponse>>> findAllAnswers(@PathVariable Long questionId) { | ||
List<FindAnswerResponse> findAnswerResponses = answerService.findAllAnswer(questionId); | ||
return ResponseEntityFactory.toResponseEntity(ANSWERS_ALL_FOUND, findAnswerResponses); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/com/kernel360/kernelsquare/domain/answer/dto/FindAnswerResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.kernel360.kernelsquare.domain.answer.dto; | ||
|
||
import com.kernel360.kernelsquare.domain.answer.entity.Answer; | ||
import lombok.Builder; | ||
|
||
import java.time.LocalDate; | ||
|
||
@Builder | ||
public record FindAnswerResponse( | ||
Long id, | ||
Long questionId, | ||
String content, | ||
String rankImageUrl, | ||
String memberImageUrl, | ||
String createdBy, | ||
String answerImageUrl, | ||
String createdDate, | ||
Long voteCount | ||
) { | ||
public static FindAnswerResponse from(Answer answer) { | ||
return new FindAnswerResponse( | ||
answer.getId(), | ||
answer.getQuestion().getId(), | ||
answer.getContent(), | ||
"rankUrl", | ||
answer.getMember().getImageUrl(), | ||
answer.getMember().getNickname(), | ||
answer.getImageUrl(), | ||
answer.getCreatedDate().toLocalDate().toString(), | ||
answer.getVoteCount() | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/com/kernel360/kernelsquare/domain/answer/repository/AnswerRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.kernel360.kernelsquare.domain.answer.repository; | ||
|
||
import com.kernel360.kernelsquare.domain.answer.entity.Answer; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.query.Param; | ||
|
||
import java.util.List; | ||
|
||
public interface AnswerRepository extends JpaRepository<Answer, Long> { | ||
@Query("SELECT a FROM answer a WHERE a.question.id = :questionId ORDER BY a.createdDate DESC") | ||
List<Answer> findAnswersByQuestionIdSortedByCreationDate(@Param("questionId") Long questionId); | ||
} |
25 changes: 25 additions & 0 deletions
25
src/main/java/com/kernel360/kernelsquare/domain/answer/service/AnswerService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.kernel360.kernelsquare.domain.answer.service; | ||
|
||
import com.kernel360.kernelsquare.domain.answer.dto.FindAnswerResponse; | ||
import com.kernel360.kernelsquare.domain.answer.repository.AnswerRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class AnswerService { | ||
private final AnswerRepository answerRepository; | ||
|
||
@Transactional(readOnly = true) | ||
public List<FindAnswerResponse> findAllAnswer(Long questionId) { | ||
return answerRepository.findAnswersByQuestionIdSortedByCreationDate(questionId) | ||
.stream() | ||
.map(FindAnswerResponse::from) | ||
.toList(); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...a/com/kernel360/kernelsquare/global/common_response/response/code/AnswerResponseCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.kernel360.kernelsquare.global.common_response.response.code; | ||
|
||
import com.kernel360.kernelsquare.global.common_response.service.code.AnswerServiceStatus; | ||
import com.kernel360.kernelsquare.global.common_response.service.code.ServiceStatus; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@RequiredArgsConstructor | ||
public enum AnswerResponseCode implements ResponseCode { | ||
ANSWER_CREATION_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, | ||
AnswerServiceStatus.ANSWER_CREATION_NOT_AUTHORIZED, "답변을 입력할 권한이 없습니다."), | ||
ANSWER_UPDATE_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, | ||
AnswerServiceStatus.ANSWER_UPDATE_NOT_AUTHORIZED, "답변을 수정할 권한이 없습니다."), | ||
|
||
ANSWER_CREATED(HttpStatus.CREATED, AnswerServiceStatus.ANSWER_CREATED, "답변 생성 성공"), | ||
ANSWERS_ALL_FOUND(HttpStatus.OK, AnswerServiceStatus.ANSWERS_ALL_FOUND, "질문에 대한 모든 답변 조회 성공"), | ||
ANSWER_UPDATED(HttpStatus.OK, AnswerServiceStatus.ANSWER_UPDATED, "답변 수정 성공"), | ||
ANSWER_DELETED(HttpStatus.OK, AnswerServiceStatus.ANSWER_DELETED, "답변 삭제 성공"), | ||
VOTE_CREATED(HttpStatus.CREATED, AnswerServiceStatus.VOTE_CREATED, "투표 생성"), | ||
VOTE_DELETED(HttpStatus.OK, AnswerServiceStatus.VOTE_DELETED, "투표 삭제"); | ||
|
||
private final HttpStatus code; | ||
private final ServiceStatus serviceStatus; | ||
private final String msg; | ||
|
||
@Override | ||
public HttpStatus getStatus() { | ||
return code; | ||
} | ||
|
||
@Override | ||
public Integer getCode() { | ||
return serviceStatus.getServiceStatus(); | ||
} | ||
|
||
@Override | ||
public String getMsg() { | ||
return msg; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...a/com/kernel360/kernelsquare/global/common_response/service/code/AnswerServiceStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.kernel360.kernelsquare.global.common_response.service.code; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequiredArgsConstructor | ||
public enum AnswerServiceStatus implements ServiceStatus{ | ||
ANSWER_CREATION_NOT_AUTHORIZED(2120), | ||
ANSWER_UPDATE_NOT_AUTHORIZED(2121), | ||
|
||
ANSWER_CREATED(2150), | ||
ANSWERS_ALL_FOUND(2151), | ||
ANSWER_UPDATED(2152), | ||
ANSWER_DELETED(2153), | ||
VOTE_CREATED(2154), | ||
VOTE_DELETED(2155); | ||
|
||
private final Integer code; | ||
|
||
@Override | ||
public Integer getServiceStatus() { | ||
return code; | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
src/test/java/com/kernel360/kernelsquare/domain/answer/controller/AnswerControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package com.kernel360.kernelsquare.domain.answer.controller; | ||
|
||
import com.kernel360.kernelsquare.domain.answer.dto.FindAnswerResponse; | ||
import com.kernel360.kernelsquare.domain.answer.entity.Answer; | ||
import com.kernel360.kernelsquare.domain.answer.service.AnswerService; | ||
import com.kernel360.kernelsquare.domain.member.entity.Member; | ||
import com.kernel360.kernelsquare.domain.question.entity.Question; | ||
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.WebMvcTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.test.context.support.WithMockUser; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
|
||
import java.time.LocalDate; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static com.kernel360.kernelsquare.global.common_response.response.code.AnswerResponseCode.ANSWERS_ALL_FOUND; | ||
import static org.mockito.ArgumentMatchers.anyLong; | ||
import static org.mockito.Mockito.*; | ||
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.MockMvcResultMatchers.*; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; | ||
|
||
@DisplayName("답변 컨트롤러 단위 테스트") | ||
@WebMvcTest(AnswerController.class) | ||
public class AnswerControllerTest { | ||
@Autowired | ||
private MockMvc mockMvc; | ||
@MockBean | ||
private AnswerService answerService; | ||
private final Long testQuestionId = 1L; | ||
private final Question testQuestion = Question | ||
.builder() | ||
.title("Test Question") | ||
.content("Test Content") | ||
.imageUrl("S3:TestImage") | ||
.closedStatus(false) | ||
.build(); | ||
|
||
private final Member testMember = Member | ||
.builder() | ||
.nickname("hongjugwang") | ||
.email("[email protected]") | ||
.password("hashedPassword") | ||
.experience(10000L) | ||
.introduction("hi, i'm hongjugwang.") | ||
.imageUrl("s3:qwe12fasdawczx") | ||
.build(); | ||
|
||
private final Answer testAnswer = Answer | ||
.builder() | ||
.content("Test Answer Content") | ||
.voteCount(10L) | ||
.imageUrl("s3:AnswerImageURL") | ||
.member(testMember) | ||
.question(testQuestion) | ||
.build(); | ||
|
||
private final FindAnswerResponse findAnswerResponse = FindAnswerResponse | ||
.builder() | ||
.id(1L) | ||
.questionId(1L) | ||
.content(testAnswer.getContent()) | ||
.rankImageUrl("s3:RankURL") | ||
.createdBy("HongJuGwang") | ||
.answerImageUrl(testAnswer.getImageUrl()) | ||
.memberImageUrl(testMember.getImageUrl()) | ||
.createdDate(LocalDate.now().toString()) | ||
.voteCount(testAnswer.getVoteCount()) | ||
.build(); | ||
|
||
private List<FindAnswerResponse> answerResponseList = new ArrayList<>(); | ||
|
||
@Test | ||
@WithMockUser | ||
@DisplayName("답변 조회 성공시, 200 OK, 메시지, 답변정보를 반환한다.") | ||
void testFindAllAnswers() throws Exception { | ||
//given | ||
answerResponseList.add(findAnswerResponse); | ||
doReturn(answerResponseList) | ||
.when(answerService) | ||
.findAllAnswer(anyLong()); | ||
|
||
//when & then | ||
mockMvc.perform(get("/api/v1/questions/" + testQuestionId + "/answers") | ||
.with(csrf()) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.accept(MediaType.APPLICATION_JSON) | ||
.characterEncoding("UTF-8")) | ||
.andExpect(status().is(ANSWERS_ALL_FOUND.getStatus().value())) | ||
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) | ||
.andExpect(jsonPath("$.code").value(ANSWERS_ALL_FOUND.getCode())) | ||
.andExpect(jsonPath("$.msg").value(ANSWERS_ALL_FOUND.getMsg())) | ||
.andExpect(jsonPath("$.data[0].content").value(testAnswer.getContent())) | ||
.andExpect(jsonPath("$.data[0].answer_image_url").value(testAnswer.getImageUrl())) | ||
.andExpect(jsonPath("$.data[0].vote_count").value(testAnswer.getVoteCount())); | ||
|
||
//verify | ||
verify(answerService, times(1)).findAllAnswer(anyLong()); | ||
} | ||
} |
Oops, something went wrong.