Skip to content

Commit

Permalink
러너 게시글 전체 조회 API 페이징 기능 추가 (#344)
Browse files Browse the repository at this point in the history
* 오류나는 Dockerfile 수정 (#295)

* refactor: 필요없는 러너 서포터 필드 삭제

* refactor: 필요없는 ChattingCount 필드 삭제

* feat: RunnerTechnicalTag 관련 클래스 추가

* refactor: SupporterTechnicalTag 빌더 생성자 접근제한자 private 으로 변경

* feat: SupporterRunnerPost 엔티티 생성

* refactor: dev CI 스크립트 변경

* refactor: dev CD 스크립트 변경

* refactor: deploy CI 스크립트 변경

* refactor: deploy CD 스크립트 변경

* Auth ArgumentResolver 익명 null 반환 변경 (#271)

* refactor: Auth ArgumentResolver 익명 null 반환 변경

* refactor: AuthSupporterPrincipal 내부 속성 변경

* refactor: docker container timezone 변경

* feat: Dockerfile 타임존 설정

* feat: jar 타임존 설정

* fix: Dockerfile 오류 해결

---------

Co-authored-by: HyunSeo Park (Hyena) <[email protected]>

* feat: 러너 게시글 전체 조회 API 구현

* fix: conflict 해결

* test: 테스트 수정

* fix: conflict 해결

* refactor: 피드백 반영

* refactor: collectApplicantCounts 메서드 로직 수정

* refactor: collectApplicantCounts 메서드 로직 수정

* refactor: 피드백 반영

* fix: 지원한 서포터가 아무도 없을 때 applicants를 계산하는 로직 수정

* refactor: queryParameters 추가

* refactor: queryParameters 추가

---------

Co-authored-by: Jeonghoon Park <[email protected]>
Co-authored-by: HyunSeo Park (Hyena) <[email protected]>
  • Loading branch information
3 people authored Aug 16, 2023
1 parent 213ea51 commit 8959876
Show file tree
Hide file tree
Showing 16 changed files with 169 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import touch.baton.domain.oauth.controller.resolver.AuthSupporterPrincipal;
import touch.baton.domain.runner.Runner;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.controller.response.RunnerPostReadResponses;
import touch.baton.domain.runnerpost.controller.response.RunnerPostResponse;
import touch.baton.domain.runnerpost.service.RunnerPostService;
import touch.baton.domain.runnerpost.service.dto.RunnerPostApplicantCreateRequest;
Expand Down Expand Up @@ -144,21 +143,22 @@ public ResponseEntity<Void> update(@AuthRunnerPrincipal final Runner runner,
}

@GetMapping
public ResponseEntity<RunnerPostReadResponses.NoFiltering> readAllRunnerPosts() {
final List<RunnerPostResponse.Simple> responses = runnerPostService.readAllRunnerPosts().stream()
.map(RunnerPostResponse.Simple::from)
.toList();
public ResponseEntity<PageResponse<RunnerPostResponse.Simple>> readAllRunnerPosts(@PageableDefault(size = 10, page = 1, sort = "createdAt", direction = DESC) final Pageable pageable) {
final Page<RunnerPost> pageRunnerPosts = runnerPostService.readAllRunnerPosts(pageable);
final List<RunnerPost> foundRunnerPosts = pageRunnerPosts.getContent();
final List<Long> applicantCounts = collectApplicantCounts(pageRunnerPosts);
final List<RunnerPostResponse.Simple> responses = IntStream.range(0, foundRunnerPosts.size())
.mapToObj(index -> {
final RunnerPost runnerPost = foundRunnerPosts.get(index);
Long applicantCount = applicantCounts.get(index);

return ResponseEntity.ok(RunnerPostReadResponses.NoFiltering.from(responses));
}
return RunnerPostResponse.Simple.from(runnerPost, applicantCount);
}).toList();

@GetMapping("/test")
public ResponseEntity<RunnerPostReadResponses.NoFiltering> readAllRunnerPostsVersionTest() {
final List<RunnerPostResponse.Simple> responses = runnerPostService.readAllRunnerPosts().stream()
.map(RunnerPostResponse.Simple::from)
.toList();
final Page<RunnerPostResponse.Simple> pageResponse
= new PageImpl<>(responses, pageable, pageRunnerPosts.getTotalPages());

return ResponseEntity.ok(RunnerPostReadResponses.NoFiltering.from(responses));
return ResponseEntity.ok(PageResponse.from(pageResponse));
}

@GetMapping("/search")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,21 @@ public record Simple(Long runnerPostId,
String title,
LocalDateTime deadline,
int watchedCount,
long applicantCount,
String reviewStatus,
RunnerResponse.Simple runnerProfile,
List<String> tags
) {

public static Simple from(final RunnerPost runnerPost) {
public static Simple from(final RunnerPost runnerPost,
final long applicantCount
) {
return new Simple(
runnerPost.getId(),
runnerPost.getTitle().getValue(),
runnerPost.getDeadline().getValue(),
runnerPost.getWatchedCount().getValue(),
applicantCount,
runnerPost.getReviewStatus().name(),
RunnerResponse.Simple.from(runnerPost.getRunner()),
convertToTags(runnerPost)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public interface RunnerPostRepository extends JpaRepository<RunnerPost, Long> {
""")
Optional<RunnerPost> joinMemberByRunnerPostId(@Param("runnerPostId") final Long runnerPostId);

List<RunnerPost> findAllByOrderByCreatedAtDesc();
Page<RunnerPost> findAll(final Pageable pageable);

List<RunnerPost> findByRunnerId(Long runnerId);
List<RunnerPost> findByRunnerId(final Long runnerId);

@Query(countQuery = """
select count(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ public Long createRunnerPostApplicant(final Supporter supporter,
return supporterRunnerPostRepository.save(runnerPostApplicant).getId();
}

public List<RunnerPost> readAllRunnerPosts() {
return runnerPostRepository.findAllByOrderByCreatedAtDesc();
public Page<RunnerPost> readAllRunnerPosts(final Pageable pageable) {
return runnerPostRepository.findAll(pageable);
}

public List<RunnerPost> readRunnerPostsByRunnerId(final Long runnerId) {
Expand All @@ -249,7 +249,18 @@ public Page<RunnerPost> readRunnerPostsBySupporterIdAndReviewStatus(final Pageab
}

public List<Long> readCountsByRunnerPostIds(final List<Long> runnerPostIds) {
return supporterRunnerPostRepository.countByRunnerPostIdIn(runnerPostIds);
final List<Long> applicantCounts = supporterRunnerPostRepository.countByRunnerPostIdIn(runnerPostIds);
if (applicantCounts.isEmpty()) {
initApplicantCounts(runnerPostIds, applicantCounts);
}

return applicantCounts;
}

private void initApplicantCounts(final List<Long> runnerPostIds, final List<Long> applicantCounts) {
for (int i = 0; i < runnerPostIds.size(); i++) {
applicantCounts.add(0L);
}
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ private RunnerPostAssuredSupport() {
return 페이징된_서포터가_연관된_러너_게시글_응답;
}

public static PageResponse<RunnerPostResponse.Simple> 러너_게시글_전체_조회_응답(final Pageable 페이징_정보,
final List<RunnerPostResponse.Simple> 러너_게시글_목록
) {
final Page<RunnerPostResponse.Simple> 페이징된_러너_게시글 = new PageImpl<>(러너_게시글_목록, 페이징_정보, 러너_게시글_목록.size());
final PageResponse<RunnerPostResponse.Simple> 페이징된_러너_게시글_응답 = PageResponse.from(페이징된_러너_게시글);

return 페이징된_러너_게시글_응답;
}

public static class RunnerPostClientRequestBuilder {

private ExtractableResponse<Response> response;
Expand Down Expand Up @@ -110,6 +119,16 @@ public static class RunnerPostClientRequestBuilder {
return this;
}

public RunnerPostClientRequestBuilder 전체_러너_게시글_페이징을_조회한다(final Pageable 페이징_정보) {
final Map<String, Object> queryParams = Map.of(
"size", 페이징_정보.getPageSize(),
"page", 페이징_정보.getPageNumber()
);

response = AssuredSupport.get("/api/v1/posts/runner", queryParams);
return this;
}

public RunnerPostClientRequestBuilder 서포터가_리뷰를_완료하고_리뷰완료_버튼을_누른다(final Long 게시글_식별자) {
response = AssuredSupport.patch("/api/v1/posts/runner/{runnerPostId}/done", "runnerPostId", 게시글_식별자, accessToken);
return this;
Expand Down Expand Up @@ -169,10 +188,13 @@ public RunnerPostServerResponseBuilder(final ExtractableResponse<Response> respo
softly.assertThat(actual.applicantCount()).isEqualTo(러너_게시글_응답.applicantCount());
softly.assertThat(actual.reviewStatus()).isEqualTo(러너_게시글_응답.reviewStatus());
softly.assertThat(actual.tags()).isEqualTo(러너_게시글_응답.tags());
softly.assertThat(actual.deadline()).isEqualToIgnoringSeconds(러너_게시글_응답.deadline());
softly.assertThat(actual.runnerProfile().name()).isEqualTo(러너_게시글_응답.runnerProfile().name());
softly.assertThat(actual.runnerProfile().company()).isEqualTo(러너_게시글_응답.runnerProfile().company());
softly.assertThat(actual.runnerProfile().imageUrl()).isEqualTo(러너_게시글_응답.runnerProfile().imageUrl());
softly.assertThat(actual.runnerProfile().runnerId()).isEqualTo(러너_게시글_응답.runnerProfile().runnerId());
softly.assertThat(actual.watchedCount()).isEqualTo(러너_게시글_응답.watchedCount());
softly.assertThat(actual.runnerPostId()).isEqualTo(러너_게시글_응답.runnerPostId());
}
);
}
Expand All @@ -188,6 +210,18 @@ public RunnerPostServerResponseBuilder(final ExtractableResponse<Response> respo
);
}

public void 전체_러너_게시글_페이징_조회_성공을_검증한다(final PageResponse<RunnerPostResponse.Simple> 전체_러너_게시글_페이징_응답) {
final PageResponse<RunnerPostResponse.Simple> actual = this.response.as(new TypeRef<PageResponse<RunnerPostResponse.Simple>>() {

});

assertSoftly(softly -> {
softly.assertThat(this.response.statusCode()).isEqualTo(HttpStatus.OK.value());
softly.assertThat(actual.data()).isEqualTo(전체_러너_게시글_페이징_응답.data());
}
);
}

public void 러너_게시글_삭제_성공을_검증한다(final HttpStatus HTTP_STATUS) {
assertThat(response.statusCode()).isEqualTo(HTTP_STATUS.value());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package touch.baton.assure.runnerpost;

import org.junit.jupiter.api.Test;
import org.springframework.data.domain.PageRequest;
import touch.baton.config.AssuredTestConfig;
import touch.baton.domain.common.response.PageResponse;
import touch.baton.domain.member.Member;
import touch.baton.domain.runner.Runner;
import touch.baton.domain.runnerpost.RunnerPost;
import touch.baton.domain.runnerpost.controller.response.RunnerPostResponse;
import touch.baton.fixture.domain.MemberFixture;
import touch.baton.fixture.domain.RunnerFixture;
import touch.baton.fixture.domain.RunnerPostFixture;

import java.util.List;

import static java.time.LocalDateTime.now;
import static touch.baton.assure.runnerpost.RunnerPostAssuredSupport.러너_게시글_전체_조회_응답;
import static touch.baton.fixture.vo.DeadlineFixture.deadline;
import static touch.baton.fixture.vo.IntroductionFixture.introduction;

@SuppressWarnings("NonAsciiCharacters")
class RunnerPostReadAssuredTest extends AssuredTestConfig {

@Test
void 러너_게시글_전체_조회에_성공한다() {
final Runner 러너_에단 = 러너를_저장한다(MemberFixture.createEthan());
final RunnerPost 러너_에단의_게시글 = 러너_게시글을_등록한다(러너_에단);
runnerPostRepository.save(러너_에단의_게시글);

final String 에단_액세스_토큰 = login(러너_에단.getMember().getSocialId().getValue());

final PageRequest 페이징_정보 = PageRequest.of(1, 10);
final RunnerPostResponse.Simple 게시글_응답
= RunnerPostResponse.Simple.from(러너_에단의_게시글, 0);
final PageResponse<RunnerPostResponse.Simple> 페이징된_게시글_응답
= 러너_게시글_전체_조회_응답(페이징_정보, List.of(게시글_응답));

RunnerPostAssuredSupport
.클라이언트_요청()
.토큰으로_로그인한다(에단_액세스_토큰)
.전체_러너_게시글_페이징을_조회한다(페이징_정보)

.서버_응답()
.전체_러너_게시글_페이징_조회_성공을_검증한다(페이징된_게시글_응답);
}

private RunnerPost 러너_게시글을_등록한다(final Runner 러너) {
return runnerPostRepository.save(RunnerPostFixture.create(러너, deadline(now().plusHours(100))));
}

private Runner 러너를_저장한다(final Member member) {
final Member 저장된_사용자 = memberRepository.save(member);

return runnerRepository.save(RunnerFixture.createRunner(introduction("안녕하세요"), 저장된_사용자));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ void readLoginMemberByAccessToken() throws Exception {
final String token = getAccessTokenBySocialId(socialId);

// when
when(oauthMemberRepository.findBySocialId(any()))
.thenReturn(Optional.ofNullable(member));
when(oauthMemberRepository.findBySocialId(any())).thenReturn(Optional.ofNullable(member));

// then
mockMvc.perform(get("/api/v1/profile/me").header(AUTHORIZATION, "Bearer " + token))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ void readMyProfileByToken() throws Exception {
final Runner runner = RunnerFixture.createRunner(MemberFixture.createHyena(), List.of(java, spring));
final String token = getAccessTokenBySocialId(runner.getMember().getSocialId().getValue());

when(oauthRunnerRepository.joinByMemberSocialId(notNull()))
.thenReturn(Optional.ofNullable(runner));
when(oauthRunnerRepository.joinByMemberSocialId(notNull())).thenReturn(Optional.ofNullable(runner));

// then
mockMvc.perform(get("/api/v1/profile/runner/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ void readMyProfileByToken() throws Exception {
final Runner runner = RunnerFixture.createRunner(MemberFixture.createHyena(), List.of(java, spring));
final String token = getAccessTokenBySocialId(runner.getMember().getSocialId().getValue());

when(oauthRunnerRepository.joinByMemberSocialId(notNull()))
.thenReturn(Optional.ofNullable(runner));
when(oauthRunnerRepository.joinByMemberSocialId(notNull())).thenReturn(Optional.ofNullable(runner));

// then
mockMvc.perform(get("/api/v1/profile/runner/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ void updateRunnerProfile() throws Exception {
final String token = getAccessTokenBySocialId(socialId);

// when
when(oauthRunnerRepository.joinByMemberSocialId(any()))
.thenReturn(Optional.ofNullable(judyRunner));
when(oauthRunnerRepository.joinByMemberSocialId(any())).thenReturn(Optional.ofNullable(judyRunner));

// then
mockMvc.perform(patch("/api/v1/profile/runner/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ void updateSupporterProfile() throws Exception {
final String token = getAccessTokenBySocialId(socialId);

// when
when(oauthSupporterRepository.joinByMemberSocialId(any()))
.thenReturn(Optional.ofNullable(supporter));
when(oauthSupporterRepository.joinByMemberSocialId(any())).thenReturn(Optional.ofNullable(supporter));

// then
mockMvc.perform(patch("/api/v1/profile/supporter/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,9 @@ void createRunnerPostApplicant() throws Exception {
// when
final RunnerPost spyRunnerPost = spy(runnerPost);
when(spyRunnerPost.getId()).thenReturn(1L);
when(runnerPostService.createRunnerPostApplicant(any(), any(), any()))
.thenReturn(1L);
when(runnerPostService.createRunnerPostApplicant(any(), any(), any())).thenReturn(1L);

when(oauthSupporterRepository.joinByMemberSocialId(any()))
.thenReturn(Optional.ofNullable(supporterHyena));
when(oauthSupporterRepository.joinByMemberSocialId(any())).thenReturn(Optional.ofNullable(supporterHyena));

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,26 @@ void readAllRunnerPosts() throws Exception {
final Tag javaTag = TagFixture.create(tagName("자바"));
final RunnerPost runnerPost = RunnerPostFixture.create(runner, deadline, List.of(javaTag));
final RunnerPost spyRunnerPost = spy(runnerPost);
given(spyRunnerPost.getId()).willReturn(1L);

// when
given(spyRunnerPost.getId()).willReturn(1L);
given(runnerPostService.readAllRunnerPosts()).willReturn(List.of(spyRunnerPost));
final List<RunnerPost> runnerPosts = List.of(spyRunnerPost);
final PageRequest pageOne = PageRequest.of(1, 10);
final PageImpl<RunnerPost> pageRunnerPosts = new PageImpl<>(runnerPosts, pageOne, runnerPosts.size());
when(runnerPostService.readAllRunnerPosts(any())).thenReturn(pageRunnerPosts);
when(runnerPostService.readCountsByRunnerPostIds(anyList())).thenReturn(List.of(1L));

// then
mockMvc.perform(get("/api/v1/posts/runner"))
mockMvc.perform(get("/api/v1/posts/runner")
.queryParam("size", String.valueOf(pageOne.getPageSize()))
.queryParam("page", String.valueOf(pageOne.getPageNumber())))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON))
.andDo(restDocs.document(
queryParameters(
parameterWithName("size").description("페이지 사이즈"),
parameterWithName("page").description("페이지 번호")
),
responseFields(
fieldWithPath("data.[].runnerPostId").type(NUMBER).description("러너 게시글 식별자값(id)"),
fieldWithPath("data.[].title").type(STRING).description("러너 게시글의 제목"),
Expand All @@ -88,7 +98,15 @@ void readAllRunnerPosts() throws Exception {
fieldWithPath("data.[].reviewStatus").type(STRING).description("러너 게시글의 리뷰 상태"),
fieldWithPath("data.[].runnerProfile.name").type(STRING).description("러너 게시글의 러너 프로필 이름"),
fieldWithPath("data.[].runnerProfile.imageUrl").type(STRING).description("러너 게시글의 러너 프로필 이미지"),
fieldWithPath("data.[].tags.[]").type(ARRAY).description("러너 게시글의 태그 목록")
fieldWithPath("data.[].tags.[]").type(ARRAY).description("러너 게시글의 태그 목록"),
fieldWithPath("data.[].applicantCount").type(NUMBER).description("러너 게시글에 신청한 서포터 수"),
fieldWithPath("pageInfo.isFirst").type(BOOLEAN).description("첫 번째 페이지인지"),
fieldWithPath("pageInfo.isLast").type(BOOLEAN).description("마지막 페이지인지"),
fieldWithPath("pageInfo.hasNext").type(BOOLEAN).description("다음 페이지가 있는지"),
fieldWithPath("pageInfo.totalPages").type(NUMBER).description("총 페이지 수"),
fieldWithPath("pageInfo.totalElements").type(NUMBER).description("총 데이터 수"),
fieldWithPath("pageInfo.currentPage").type(NUMBER).description("현재 페이지"),
fieldWithPath("pageInfo.currentSize").type(NUMBER).description("현재 페이지 데이터 수")
))
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ void readReferencedBySupporter() throws Exception {
final PageImpl<RunnerPost> pageRunnerPosts = new PageImpl<>(runnerPosts, pageOne, runnerPosts.size());
when(runnerPostService.readRunnerPostsBySupporterIdAndReviewStatus(any(), any(), any()))
.thenReturn(pageRunnerPosts);
when(runnerPostService.readCountsByRunnerPostIds(anyList()))
.thenReturn(List.of(1L));
when(runnerPostService.readCountsByRunnerPostIds(anyList())).thenReturn(List.of(1L));

// then
mockMvc.perform(get("/api/v1/posts/runner/search")
Expand Down
Loading

0 comments on commit 8959876

Please sign in to comment.