diff --git a/src/docs/asciidoc/topic.adoc b/src/docs/asciidoc/topic.adoc index f71ed57c..fbf312be 100644 --- a/src/docs/asciidoc/topic.adoc +++ b/src/docs/asciidoc/topic.adoc @@ -37,6 +37,9 @@ OK. 토큰 O / 종료된 토픽 조회 (투표 순) operation::topic-controller-test/get-topic-slice_closed[snippets="http-request,http-response"] +OK. 토픽 단 건 조회 + +operation::topic-controller-test/get_single_topic[snippets="http-request,http-response"] ### 1.3. 토픽 신고 diff --git a/src/main/java/life/offonoff/ab/application/service/TopicService.java b/src/main/java/life/offonoff/ab/application/service/TopicService.java index 53daae14..bcafec1c 100644 --- a/src/main/java/life/offonoff/ab/application/service/TopicService.java +++ b/src/main/java/life/offonoff/ab/application/service/TopicService.java @@ -159,6 +159,13 @@ public Slice findAll(final Long retrieverId, final TopicSearchReq return topics.map(topic -> TopicResponse.from(topic, retriever)); } + public TopicResponse findById(Long memberId, Long topicId) { + Member retriever = findMember(memberId); + Topic topic = findTopic(topicId); + + return TopicResponse.from(topic, retriever); + } + //== Hide ==// @Transactional public void hideTopicForMember(final Long memberId, final Long topicId, final Boolean hide) { @@ -179,8 +186,8 @@ private void doHide(final Member member, final Topic topic) { private void cancelHide(final Member member, final Topic topic) { member.cancelHideIfExists(topic); } - //== Vote ==// + //== Vote ==// @Transactional public VoteResponse voteForTopicByMember(final Long topicId, final Long memberId, final VoteRequest request) { Member member = findMember(memberId); diff --git a/src/main/java/life/offonoff/ab/web/TopicController.java b/src/main/java/life/offonoff/ab/web/TopicController.java index 1300aa3c..ddc4052c 100644 --- a/src/main/java/life/offonoff/ab/web/TopicController.java +++ b/src/main/java/life/offonoff/ab/web/TopicController.java @@ -54,6 +54,15 @@ public ResponseEntity> getTopicInfos( return ResponseEntity.ok(PageResponse.of(topicService.findAll(memberId, request, pageable))); } + /** + * 단 건 Topic 조회 + */ + @GetMapping("/{topicId}") + public ResponseEntity getTopic(@Authorized(nullable = true) Long memberId, + @PathVariable Long topicId) { + return ResponseEntity.ok(topicService.findById(memberId, topicId)); + } + @PatchMapping("/{topicId}/hide") public ResponseEntity hideTopic( @Authorized Long memberId, diff --git a/src/test/java/life/offonoff/ab/web/TopicControllerTest.java b/src/test/java/life/offonoff/ab/web/TopicControllerTest.java index a9255bb6..075ea776 100644 --- a/src/test/java/life/offonoff/ab/web/TopicControllerTest.java +++ b/src/test/java/life/offonoff/ab/web/TopicControllerTest.java @@ -16,20 +16,19 @@ import life.offonoff.ab.domain.member.Member; import life.offonoff.ab.domain.topic.Topic; import life.offonoff.ab.domain.topic.TopicSide; +import life.offonoff.ab.domain.topic.TopicStatus; import life.offonoff.ab.domain.topic.choice.Choice; import life.offonoff.ab.domain.topic.choice.ChoiceOption; import life.offonoff.ab.domain.topic.choice.content.ImageTextChoiceContent; -import life.offonoff.ab.domain.vote.Vote; import life.offonoff.ab.exception.*; import life.offonoff.ab.repository.pagination.PagingUtil; import life.offonoff.ab.restdocs.RestDocsTest; import life.offonoff.ab.util.token.JwtProvider; import life.offonoff.ab.web.common.aspect.auth.AuthorizedArgumentResolver; -import life.offonoff.ab.web.response.topic.choice.ChoiceResponseWithCount; import life.offonoff.ab.web.response.CommentResponse; import life.offonoff.ab.web.response.VoteResponse; import life.offonoff.ab.web.response.topic.TopicResponse; -import life.offonoff.ab.web.response.topic.choice.ChoiceResponsesFactory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -44,7 +43,6 @@ import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -106,7 +104,7 @@ void getTopicSlice_default() throws Exception { .thenReturn(topicResponseSlice); mvc.perform( - get(TopicUri.RETRIEVE) + get(TopicUri.RETRIEVE_PAGE) .header("AUTHORIZATION", "Bearer ACCESS_TOKEN")) .andExpect(status().isOk()) .andDo(restDocs.document(queryParameters( @@ -126,7 +124,7 @@ void getTopicSlice_filtered_by_keyword() throws Exception { .thenReturn(topicSlice); mvc.perform( - get(TopicUri.RETRIEVE) + get(TopicUri.RETRIEVE_PAGE) .header("AUTHORIZATION", "Bearer ACCESS_TOKEN") .param("keyword_id", String.valueOf(1L))) .andExpect(status().isOk()); @@ -141,7 +139,7 @@ void getTopicSlice_default_for_unauthorized_member() throws Exception { when(topicService.findAll(isNull(Long.class), any(TopicSearchRequest.class), any(Pageable.class))) .thenReturn(topicResponseSlice); - mvc.perform(get(TopicUri.RETRIEVE) + mvc.perform(get(TopicUri.RETRIEVE_PAGE) .header("side", TopicSide.TOPIC_A)) .andExpect(status().isOk()); } @@ -155,7 +153,7 @@ void getTopicSlice_closed() throws Exception { .thenReturn(topicResponseSlice); mvc.perform( - get(TopicUri.RETRIEVE) + get(TopicUri.RETRIEVE_PAGE) .header("AUTHORIZATION", "Bearer ACCESS_TOKEN") .param("status", String.valueOf(CLOSED))) .andExpect(status().isOk()); @@ -490,12 +488,48 @@ private Slice createTopicSliceFilteredByKeyword(Long keywordId) { pageable); } + @Test + void get_single_topic() throws Exception { + // create author + Member author = TestMember.builder() + .id(1L) + .nickname("nicknameA") + .build().buildMember(); + + // create keyword + Keyword keyword = TestKeyword.builder() + .id(1L) + .name("key") + .build().buildKeyword(); + + // create Topic + Topic topic = TestTopic.builder() + .id(1L) + .title("title" + 1L) + .side(TopicSide.TOPIC_B) + .author(author) + .keyword(keyword) + .voteCount(1000) + .status(TopicStatus.VOTING) + .build().buildTopic(); + + when(topicService.findById(any(), any())) + .thenReturn(TopicResponse.from(topic, author)); + + mvc.perform(get(TopicUri.RETRIEVE, topic.getId()) + .header("AUTHORIZATION", "Bearer ACCESS_TOKEN")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topicId").value(topic.getId())) + .andDo(print()); + } private static class TopicUri { private static final String BASE = "/topics"; private static final String INFO = "/info"; - private static final String RETRIEVE = BASE + INFO; - private static final String RETRIEVE_IN_STATUS = BASE + INFO + "?status={}"; + private static final String GET = "/{topicId}"; + private static final String RETRIEVE = BASE + GET; + private static final String RETRIEVE_PAGE = BASE + INFO; + private static final String RETRIEVE_PAGE_IN_STATUS = BASE + INFO + "?status={}"; private static final String REPORT = BASE + "/{topicId}/report"; private static final String STATUS = BASE + "/{topicId}/status?active={active}"; private static final String VOTE = BASE + "/{topicId}/vote";