Skip to content

Commit

Permalink
러너 게시글 상세 조회 서포터 리뷰 지원 유무 응답 추가 및 uri 수정 (#365)
Browse files Browse the repository at this point in the history
* feat: Supporter 의 RunnerPost 지원 여부 확인 조회 메서드 레포지터리, 서비스 구현

* feat: RunnerPost 상세 조회시 Supporter 리뷰 지원 여부

* test: RunnerPost 상세 조회시 Supporter 지원 여부 테스트 구현

* fix: RunnerPost 에 Supporter 리뷰 지원 api 의 uri 수정

* docs: 러너 게시글 상세 조회 테스트 클래스명 수정으로 인한 adoc 파일 변경
  • Loading branch information
hyena0608 authored Aug 16, 2023
1 parent 5894dad commit 213ea51
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 86 deletions.
12 changes: 6 additions & 6 deletions backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test

===== *Http Request*

include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test/read-by-runner-post-id/http-request.adoc[]
include::{snippets}/../../build/generated-snippets/runner-post-read-one-api-test/read-by-runner-post-id/http-request.adoc[]

===== *Http Request Headers
===== *Http Request Headers*

include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test/read-by-runner-post-id/request-headers.adoc[]
include::{snippets}/../../build/generated-snippets/runner-post-read-one-api-test/read-by-runner-post-id/request-headers.adoc[]

===== *Http Request Path Parameters*

include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test/read-by-runner-post-id/path-parameters.adoc[]
include::{snippets}/../../build/generated-snippets/runner-post-read-one-api-test/read-by-runner-post-id/path-parameters.adoc[]

===== *Http Response*

include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test/read-by-runner-post-id/http-response.adoc[]
include::{snippets}/../../build/generated-snippets/runner-post-read-one-api-test/read-by-runner-post-id/http-response.adoc[]

===== *Http Response Fields*

include::{snippets}/../../build/generated-snippets/runner-post-read-all-api-test/read-by-runner-post-id/response-fields.adoc[]
include::{snippets}/../../build/generated-snippets/runner-post-read-one-api-test/read-by-runner-post-id/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import touch.baton.domain.common.vo.Contents;
import touch.baton.domain.common.vo.Title;
import touch.baton.domain.common.vo.WatchedCount;
import touch.baton.domain.member.Member;
import touch.baton.domain.runner.Runner;
import touch.baton.domain.runnerpost.exception.RunnerPostDomainException;
import touch.baton.domain.runnerpost.vo.Deadline;
Expand Down Expand Up @@ -253,6 +254,10 @@ public boolean isNotOwner(final Runner targetRunner) {
return !runner.equals(targetRunner);
}

public boolean isNotOwner(final Member targetMember) {
return !runner.getMember().equals(targetMember);
}

public boolean isReviewStatusStarted() {
return !(reviewStatus.isNotStarted() || reviewStatus.isOverdue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import touch.baton.domain.common.response.PageResponse;
import touch.baton.domain.member.Member;
import touch.baton.domain.oauth.controller.resolver.AuthMemberPrincipal;
import touch.baton.domain.oauth.controller.resolver.AuthRunnerPrincipal;
import touch.baton.domain.oauth.controller.resolver.AuthSupporterPrincipal;
import touch.baton.domain.runner.Runner;
Expand Down Expand Up @@ -71,7 +73,7 @@ public ResponseEntity<Void> createRunnerPostVersionTest(@AuthRunnerPrincipal fin
return ResponseEntity.created(redirectUri).build();
}

@PostMapping("{runnerPostId}/applicant")
@PostMapping("{runnerPostId}/application")
public ResponseEntity<Void> createRunnerPostApplicant(@AuthSupporterPrincipal final Supporter supporter,
@PathVariable final Long runnerPostId,
@RequestBody @Valid final RunnerPostApplicantCreateRequest request
Expand All @@ -87,15 +89,20 @@ public ResponseEntity<Void> createRunnerPostApplicant(@AuthSupporterPrincipal fi
}

@GetMapping("/{runnerPostId}")
public ResponseEntity<RunnerPostResponse.Detail> readByRunnerPostId(@AuthRunnerPrincipal(required = false) final Runner runner,
public ResponseEntity<RunnerPostResponse.Detail> readByRunnerPostId(@AuthMemberPrincipal(required = false) final Member member,
@PathVariable final Long runnerPostId
) {
final RunnerPost foundRunnerPost = runnerPostService.readByRunnerPostId(runnerPostId);
final long applicantCount = runnerPostService.readCountByRunnerPostId(foundRunnerPost.getId());
final boolean isApplicantHistoryExist = runnerPostService.existsRunnerPostApplicantByRunnerPostIdAndMemberId(runnerPostId, member.getId());

runnerPostService.increaseWatchedCount(foundRunnerPost);
final RunnerPostResponse.Detail response
= RunnerPostResponse.Detail.of(foundRunnerPost, foundRunnerPost.isNotOwner(runner), applicantCount);
final RunnerPostResponse.Detail response = RunnerPostResponse.Detail.of(
foundRunnerPost,
foundRunnerPost.isNotOwner(member),
isApplicantHistoryExist,
applicantCount
);

return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ public record Detail(Long runnerPostId,
long applicantCount,
ReviewStatus reviewStatus,
boolean isOwner,
boolean isApplied,
List<String> tags,
RunnerResponse.Detail runnerProfile
) {

public static Detail of(final RunnerPost runnerPost, final boolean isOwner, final long applicantCount) {
public static Detail of(final RunnerPost runnerPost,
final boolean isOwner,
final boolean isApplied,
final long applicantCount
) {
return new Detail(
runnerPost.getId(),
runnerPost.getTitle().getValue(),
Expand All @@ -33,6 +38,7 @@ public static Detail of(final RunnerPost runnerPost, final boolean isOwner, fina
applicantCount,
runnerPost.getReviewStatus(),
isOwner,
isApplied,
convertToTags(runnerPost),
RunnerResponse.Detail.from(runnerPost.getRunner())
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,7 @@ private boolean isApplySupporter(final Long runnerPostId, final Supporter foundS
return !supporterRunnerPostRepository.existsByRunnerPostIdAndSupporterId(runnerPostId, foundSupporter.getId());
}

public boolean existsRunnerPostApplicantByRunnerPostIdAndMemberId(final Long runnerPostId, final Long memberId) {
return supporterRunnerPostRepository.existsByRunnerPostIdAndMemberId(runnerPostId, memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@ having srp.runnerPost.id in (:runnerPostIds)
List<Long> countByRunnerPostIdIn(@Param("runnerPostIds") final List<Long> runnerPostIds);

@Query("""
select count(1)
from SupporterRunnerPost srp
group by srp.runnerPost.id
having srp.runnerPost.id = :runnerPostId
""")
select count(1)
from SupporterRunnerPost srp
group by srp.runnerPost.id
having srp.runnerPost.id = :runnerPostId
""")
Optional<Integer> countByRunnerPostId(@Param("runnerPostId") final Long runnerPostId);

@Query("""
select (count(1) >= 1)
from SupporterRunnerPost srp
join fetch Member m on m.id = srp.supporter.member.id
where srp.runnerPost.id = :runnerPostId
and srp.supporter.member.id = :memberId
""")
boolean existsByRunnerPostIdAndMemberId(@Param("runnerPostId") final Long runnerPostId, @Param("memberId") final Long memberId);

boolean existsByRunnerPostId(final Long runnerPostId);

void deleteBySupporterIdAndRunnerPostId(final Long supporterId, final Long runnerPostId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static class RunnerPostClientRequestBuilder {
}

public RunnerPostClientRequestBuilder 서포터가_러너_게시글에_리뷰를_신청한다(final Long 러너_게시글_식별자값, final String 리뷰_지원_메시지) {
response = AssuredSupport.post("/api/v1/posts/runner/{runnerPostId}/applicant",
response = AssuredSupport.post("/api/v1/posts/runner/{runnerPostId}/application",
accessToken,
Map.of("runnerPostId", 러너_게시글_식별자값),
new RunnerPostApplicantCreateRequest(리뷰_지원_메시지)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class RunnerPostAssuredCreateTest extends AssuredTestConfig {
.러너_게시글_생성_성공을_검증한다()
.생성한_러너_게시글의_식별자값을_반환한다();

final RunnerPostResponse.Detail 리뷰가_시작되지_않은_에단의_러너_게시글_Detail_응답 = 러너_게시글_Detail_응답을_생성한다(러너_에단, 러너_게시글_생성_요청, NOT_STARTED, 에단의_러너_게시글_식별자값, 1, 0L);
final RunnerPostResponse.Detail 리뷰가_시작되지_않은_에단의_러너_게시글_Detail_응답 = 러너_게시글_Detail_응답을_생성한다(러너_에단, 러너_게시글_생성_요청, NOT_STARTED, 에단의_러너_게시글_식별자값, 1, 0L, false);
RunnerPostAssuredSupport
.클라이언트_요청()
.토큰으로_로그인한다(에단_로그인_토큰)
Expand Down Expand Up @@ -92,7 +92,8 @@ class RunnerPostAssuredCreateTest extends AssuredTestConfig {
final ReviewStatus 리뷰_상태,
final Long 러너_게시글_식별자값,
final int 조회수,
final long 서포터_지원자수
final long 서포터_지원자수,
final boolean 서포터_지원_여부
) {
return 러너_게시글_Detail_응답(
러너_게시글_식별자값,
Expand All @@ -104,6 +105,7 @@ class RunnerPostAssuredCreateTest extends AssuredTestConfig {
서포터_지원자수,
리뷰_상태,
true,
서포터_지원_여부,
러너,
러너_게시글_생성_요청.tags()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ private RunnerPostAssuredSupport() {
final long 서포터_지원자수,
final ReviewStatus 리뷰_상태,
final boolean 주인_여부,
final boolean 서포터_지원_여부,
final Runner 러너,
final List<String> 태그_목록
) {
Expand All @@ -58,6 +59,7 @@ private RunnerPostAssuredSupport() {
서포터_지원자수,
리뷰_상태,
주인_여부,
서포터_지원_여부,
태그_목록,
RunnerResponse.Detail.from(러너)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class RunnerPostReadByRunnerPostIdAssuredTest extends AssuredTestConfig {
0L,
ReviewStatus.NOT_STARTED,
true,
false,
new ArrayList<>(),
RunnerResponse.Detail.from(러너_헤나)
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class RunnerPostReadWithLoginedAssuredTest extends AssuredTestConfig {
final RunnerPost 러너_게시글 = runnerPostRepository.save(RunnerPostFixture.create(러너_헤나, deadline(now().plusHours(100))));
final String 로그인용_토큰 = login(사용자_헤나.getSocialId().getValue());

final RunnerPostResponse.Detail 러너_게시글_detail_응답 = 러너_게시글_Detail_응답을_생성한다(러너_헤나, 러너_게시글, ReviewStatus.NOT_STARTED, 러너_게시글.getId(), 1, 0);
final RunnerPostResponse.Detail 러너_게시글_detail_응답 = 러너_게시글_Detail_응답을_생성한다(러너_헤나, 러너_게시글, ReviewStatus.NOT_STARTED, 러너_게시글.getId(), 1, 0, false);

클라이언트_요청()
.토큰으로_로그인한다(로그인용_토큰)
Expand All @@ -42,7 +42,8 @@ class RunnerPostReadWithLoginedAssuredTest extends AssuredTestConfig {
final ReviewStatus 리뷰_상태,
final Long 러너_게시글_식별자값,
final int 조회수,
final int 서포터_지원자수
final long 서포터_지원자수,
final boolean 서포터_지원_여부
) {
return 러너_게시글_Detail_응답(
러너_게시글_식별자값,
Expand All @@ -54,6 +55,7 @@ class RunnerPostReadWithLoginedAssuredTest extends AssuredTestConfig {
서포터_지원자수,
리뷰_상태,
!러너_게시글.isNotOwner(로그인한_러너),
서포터_지원_여부,
러너_게시글.getRunner(),
러너_게시글.getRunnerPostTags().getRunnerPostTags().stream()
.map(runnerPostTag -> runnerPostTag.getTag().getTagName().getValue())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void createRunnerPostApplicant() throws Exception {
// then
final RunnerPostApplicantCreateRequest request = new RunnerPostApplicantCreateRequest("안녕하세요, 서포터 헤나입니다.");

mockMvc.perform(post("/api/v1/posts/runner/{runnerPostId}/applicant", spyRunnerPost.getId())
mockMvc.perform(post("/api/v1/posts/runner/{runnerPostId}/application", spyRunnerPost.getId())
.header(AUTHORIZATION, "Bearer " + token)
.contentType(APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(request)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,14 @@

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.LocalDateTime.now;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.when;
import static org.mockito.Mockito.spy;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.JsonFieldType.ARRAY;
import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN;
Expand All @@ -45,7 +40,6 @@
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
Expand All @@ -67,64 +61,6 @@ void setUp() {
restdocsSetUp(runnerPostController);
}

@DisplayName("러너 게시글 상세 조회 API")
@Test
void readByRunnerPostId() throws Exception {
// given
final Runner runnerHyena = RunnerFixture.createRunner(MemberFixture.createHyena());

final Deadline deadline = deadline(now().plusHours(100));
final Tag javaTag = TagFixture.create(tagName("자바"));

// when
final Runner spyRunnerHyena = spy(runnerHyena);
when(spyRunnerHyena.getId()).thenReturn(1L);

final RunnerPost runnerPost = RunnerPostFixture.create(spyRunnerHyena, deadline, List.of(javaTag));
final RunnerPost spyRunnerPost = spy(runnerPost);
when(spyRunnerPost.getId()).thenReturn(1L);

when(runnerPostService.readByRunnerPostId(any()))
.thenReturn(spyRunnerPost);
when(runnerPostService.readCountByRunnerPostId(any()))
.thenReturn(3L);

final String token = getAccessTokenBySocialId(runnerHyena.getMember().getSocialId().getValue());

when(oauthRunnerRepository.joinByMemberSocialId(any()))
.thenReturn(Optional.ofNullable(runnerHyena));

// then
mockMvc.perform(get("/api/v1/posts/runner/{runnerPostId}", spyRunnerPost.getId())
.header(AUTHORIZATION, "Bearer " + token))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON))
.andDo(restDocs.document(
requestHeaders(
headerWithName(AUTHORIZATION).description("Bearer JWT (필수값 x)")
),
pathParameters(
parameterWithName("runnerPostId").description("러너 게시글 식별자값")
),
responseFields(
fieldWithPath("runnerPostId").type(NUMBER).description("러너 게시글 식별자값(id)"),
fieldWithPath("title").type(STRING).description("러너 게시글 제목"),
fieldWithPath("contents").type(STRING).description("러너 게시글 내용"),
fieldWithPath("deadline").type(STRING).description("러너 게시글 마감기한"),
fieldWithPath("isOwner").type(BOOLEAN).description("러너 게시글 주인 여부"),
fieldWithPath("applicantCount").type(NUMBER).description("러너 게시글 서포터 지원자수"),
fieldWithPath("watchedCount").type(NUMBER).description("러너 게시글 조회수"),
fieldWithPath("reviewStatus").type(STRING).description("러너 게시글 리뷰 상태"),
fieldWithPath("pullRequestUrl").type(STRING).description("러너 게시글 PR URL"),
fieldWithPath("tags").type(ARRAY).description("러너 게시글 태그 목록"),
fieldWithPath("runnerProfile.runnerId").type(NUMBER).description("러너 게시글 식별자값(id)"),
fieldWithPath("runnerProfile.name").type(STRING).description("러너 게시글 식별자값(id)"),
fieldWithPath("runnerProfile.company").type(STRING).description("러너 게시글 식별자값(id)"),
fieldWithPath("runnerProfile.imageUrl").type(STRING).description("러너 게시글 식별자값(id)")
))
);
}

@DisplayName("러너 게시글 전체 조회 API")
@Test
void readAllRunnerPosts() throws Exception {
Expand Down
Loading

0 comments on commit 213ea51

Please sign in to comment.