Skip to content

Commit

Permalink
서포터 프로필 조회 API 구현 (#298)
Browse files Browse the repository at this point in the history
* test: 인수 테스트 Jwt Mocking 추가

* feat: 서포터 프로필 응답 객체 추가

* test: 서포터 프로필 조회 인수 테스트 추가

* feat: 서포터와 사용자 패치 조인 기능 추가

* feat: 서포터 프로필 조회 서비스 기능 추가

* feat: 서포터 프로필 조회 API 추가
  • Loading branch information
hyena0608 authored Aug 9, 2023
1 parent 41a8766 commit 9ec71e3
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package touch.baton.domain.supporter.controller;

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 touch.baton.domain.supporter.Supporter;
import touch.baton.domain.supporter.controller.response.SupporterResponse;
import touch.baton.domain.supporter.service.SupporterService;

@RequiredArgsConstructor
@RequestMapping("/api/v1/profile/supporter")
@RestController
public class SupporterProfileController {

private final SupporterService supporterService;

@GetMapping("/{supporterId}")
public ResponseEntity<SupporterResponse.Profile> readProfileBySupporterId(@PathVariable final Long supporterId) {
final Supporter foundSupporter = supporterService.readBySupporterId(supporterId);
final SupporterResponse.Profile response = SupporterResponse.Profile.from(foundSupporter);

return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package touch.baton.domain.supporter.controller.response;

import touch.baton.domain.supporter.Supporter;
import touch.baton.domain.technicaltag.SupporterTechnicalTag;

import java.util.List;

Expand All @@ -24,16 +23,36 @@ public static Detail from(final Supporter supporter) {
supporter.getReviewCount().getValue(),
supporter.getMember().getGithubUrl().getValue(),
supporter.getIntroduction().getValue(),
getTechnicalTagsName(supporter)
convertToTechnicalTags(supporter)
);
}
}

private static List<String> getTechnicalTagsName(final Supporter supporter) {
return supporter.getSupporterTechnicalTags().getSupporterTechnicalTags()
.stream()
.map(SupporterTechnicalTag::getTechnicalTag)
.map(technicalTag -> technicalTag.getTagName().getValue())
.toList();
public record Profile(Long supporterId,
String name,
String company,
String imageUrl,
String githubUrl,
String introduction,
List<String> technicalTags
) {
public static SupporterResponse.Profile from(final Supporter supporter) {
return new SupporterResponse.Profile(
supporter.getId(),
supporter.getMember().getMemberName().getValue(),
supporter.getMember().getCompany().getValue(),
supporter.getMember().getImageUrl().getValue(),
supporter.getMember().getGithubUrl().getValue(),
supporter.getIntroduction().getValue(),
convertToTechnicalTags(supporter)
);
}
}

private static List<String> convertToTechnicalTags(final Supporter supporter) {
return supporter.getSupporterTechnicalTags().getSupporterTechnicalTags()
.stream()
.map(supporterTechnicalTag -> supporterTechnicalTag.getTechnicalTag().getTagName().getValue())
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package touch.baton.domain.supporter.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import touch.baton.domain.supporter.Supporter;

import java.util.Optional;

public interface SupporterRepository extends JpaRepository<Supporter, Long> {

@Query("""
select s
from Supporter s
join fetch Member m on s.member.id = m.id
where s.id = :supporterId
""")
Optional<Supporter> joinMemberBySupporterId(@Param("supporterId") final Long supporterId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import touch.baton.domain.supporter.Supporter;
import touch.baton.domain.supporter.exception.SupporterBusinessException;
import touch.baton.domain.supporter.repository.SupporterRepository;

import java.util.List;
Expand All @@ -18,4 +19,9 @@ public class SupporterService {
public List<Supporter> readAllSupporters() {
return supporterRepository.findAll();
}

public Supporter readBySupporterId(final Long supporterId) {
return supporterRepository.joinMemberBySupporterId(supporterId)
.orElseThrow(() -> new SupporterBusinessException("존재하지 않는 서포터 식별자값으로 조회할 수 없습니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ public static ExtractableResponse<Response> post(final String uri, final Object
.extract();
}

public static ExtractableResponse<Response> get(final String uri,
final String pathParamName,
final Long id,
final String accessToken
) {
return RestAssured
.given().log().ifValidationFails()
.auth().preemptive().oauth2(accessToken)
.when().log().ifValidationFails()
.pathParam(pathParamName, id)
.get(uri)
.then().log().ifError()
.extract();
}

public static ExtractableResponse<Response> get(final String uri, final String pathParamName, final Long id) {
return RestAssured
.given().log().ifValidationFails()
Expand All @@ -32,7 +47,7 @@ public static ExtractableResponse<Response> get(final String uri, final String p
public static ExtractableResponse<Response> get(final String uri, final String accessToken) {
return RestAssured
.given().log().ifValidationFails()
.header("authorization", "Bearer " + accessToken)
.auth().preemptive().oauth2(accessToken)
.when().log().ifValidationFails()
.get(uri)
.then().log().ifError()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package touch.baton.assure.runnerpost;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import touch.baton.config.AssuredTestConfig;
import touch.baton.domain.member.Member;
Expand All @@ -23,25 +22,29 @@
@SuppressWarnings("NonAsciiCharacters")
class RunnerPostAssuredReadTest extends AssuredTestConfig {

@Disabled
@Test
void 러너의_게시글_식별자값으로_러너_게시글_상세_정보_조회에_성공한다() {
final Member memberHyena = memberRepository.save(MemberFixture.createHyena());
final Runner runnerHyena = runnerRepository.save(RunnerFixture.create(introduction("안녕하세요"), memberHyena));
final RunnerPost runnerPost = runnerPostRepository.save(RunnerPostFixture.create(runnerHyena, deadline(LocalDateTime.now().plusHours(100))));
final Member 사용자_헤나 = memberRepository.save(MemberFixture.createHyena());
final Runner 러너_헤나 = runnerRepository.save(RunnerFixture.create(introduction("안녕하세요"), 사용자_헤나));
final RunnerPost 러너_게시글 = runnerPostRepository.save(RunnerPostFixture.create(러너_헤나, deadline(LocalDateTime.now().plusHours(100))));
final String 로그인용_토큰 = login(사용자_헤나.getSocialId().getValue());

RunnerPostAssuredSupport
.클라이언트_요청().러너_게시글_식별자값으로_러너_게시글을_조회한다(runnerPost.getId())
.서버_응답().러너_게시글_단건_조회_성공을_검증한다(new RunnerPostResponse.Detail(
runnerPost.getId(),
runnerPost.getTitle().getValue(),
runnerPost.getContents().getValue(),
runnerPost.getPullRequestUrl().getValue(),
runnerPost.getDeadline().getValue(),
.클라이언트_요청()
.토큰으로_로그인한다(로그인용_토큰)
.러너_게시글_식별자값으로_러너_게시글을_조회한다(러너_게시글.getId())

.서버_응답()
.러너_게시글_단건_조회_성공을_검증한다(new RunnerPostResponse.Detail(
러너_게시글.getId(),
러너_게시글.getTitle().getValue(),
러너_게시글.getContents().getValue(),
러너_게시글.getPullRequestUrl().getValue(),
러너_게시글.getDeadline().getValue(),
watchedCount(1).getValue(),
ReviewStatus.NOT_STARTED,
true,
RunnerResponse.Detail.from(runnerHyena),
RunnerResponse.Detail.from(러너_헤나),
Collections.emptyList()
));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ public static class RunnerPostClientRequestBuilder {

private ExtractableResponse<Response> response;

private String accessToken;

public RunnerPostClientRequestBuilder 토큰으로_로그인한다(final String accessToken) {
this.accessToken = accessToken;
return this;
}

public RunnerPostClientRequestBuilder 러너_게시글_식별자값으로_러너_게시글을_조회한다(final Long 러너_게시글_식별자값) {
response = AssuredSupport.get("/api/v1/posts/runner/{runnerPostId}", "runnerPostId", 러너_게시글_식별자값);
response = AssuredSupport.get("/api/v1/posts/runner/{runnerPostId}", "runnerPostId", 러너_게시글_식별자값, accessToken);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package touch.baton.assure.supporter;

import org.junit.jupiter.api.Test;
import touch.baton.config.AssuredTestConfig;
import touch.baton.domain.member.Member;
import touch.baton.domain.supporter.Supporter;
import touch.baton.fixture.domain.MemberFixture;
import touch.baton.fixture.domain.SupporterFixture;

import static touch.baton.assure.supporter.SupporterProfileAssuredSupport.서포터_프로필_응답;

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

@Test
void 서포터_프로필을_조회한다() {
final Member 사용자_헤나 = memberRepository.save(MemberFixture.createHyena());
final Supporter 서포터_헤나 = supporterRepository.save(SupporterFixture.create(사용자_헤나));

SupporterProfileAssuredSupport
.클라이언트_요청()
.서포터_프로필을_서포터_식별자값으로_조회한다(서포터_헤나.getId())

.서버_응답()
.서포터_프로필_조회_성공을_검증한다(서포터_프로필_응답(서포터_헤나));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package touch.baton.assure.supporter;

import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import touch.baton.assure.common.AssuredSupport;
import touch.baton.domain.supporter.Supporter;
import touch.baton.domain.supporter.controller.response.SupporterResponse;

import static org.assertj.core.api.SoftAssertions.assertSoftly;

public class SupporterProfileAssuredSupport {

private SupporterProfileAssuredSupport() {
}

public static SupporterClientRequestBuilder 클라이언트_요청() {
return new SupporterClientRequestBuilder();
}

public static SupporterResponse.Profile 서포터_프로필_응답(final Supporter 서포터) {
return SupporterResponse.Profile.from(서포터);
}

public static class SupporterClientRequestBuilder {

private ExtractableResponse<Response> response;

public SupporterClientRequestBuilder 서포터_프로필을_서포터_식별자값으로_조회한다(final Long 서포터_식별자값) {
response = AssuredSupport.get("/api/v1/profile/supporter/{supporterId}", "supporterId", 서포터_식별자값);
return this;
}

public SupporterServerResponseBuilder 서버_응답() {
return new SupporterServerResponseBuilder(response);
}
}

public static class SupporterServerResponseBuilder {

private final ExtractableResponse<Response> response;

public SupporterServerResponseBuilder(final ExtractableResponse<Response> response) {
this.response = response;
}

public void 서포터_프로필_조회_성공을_검증한다(final SupporterResponse.Profile 서포터_프로필_응답) {
final SupporterResponse.Profile actual = this.response.as(SupporterResponse.Profile.class);

assertSoftly(softly -> {
softly.assertThat(actual.supporterId()).isEqualTo(서포터_프로필_응답.supporterId());
softly.assertThat(actual.name()).isEqualTo(서포터_프로필_응답.name());
softly.assertThat(actual.company()).isEqualTo(서포터_프로필_응답.company());
softly.assertThat(actual.imageUrl()).isEqualTo(서포터_프로필_응답.imageUrl());
softly.assertThat(actual.githubUrl()).isEqualTo(서포터_프로필_응답.githubUrl());
softly.assertThat(actual.introduction()).isEqualTo(서포터_프로필_응답.introduction());
softly.assertThat(actual.technicalTags()).isEqualTo(서포터_프로필_응답.technicalTags());
}
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package touch.baton.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.server.LocalServerPort;
import touch.baton.domain.member.repository.MemberRepository;
import touch.baton.domain.runner.repository.RunnerRepository;
import touch.baton.domain.runnerpost.repository.RunnerPostRepository;
import touch.baton.domain.supporter.repository.SupporterRepository;
import touch.baton.infra.auth.jwt.JwtDecoder;

import java.util.UUID;

import static org.mockito.BDDMockito.when;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Expand All @@ -28,8 +36,21 @@ public abstract class AssuredTestConfig {
@Autowired
protected SupporterRepository supporterRepository;

@MockBean
private JwtDecoder jwtDecoder;

@BeforeEach
void assuredTestSetUp(@LocalServerPort int port) {
RestAssured.port = port;
}

public String login(final String socialId) {
final String token = UUID.randomUUID().toString();
final Claims claims = Jwts.claims();
claims.put("socialId", socialId);

when(jwtDecoder.parseJwtToken(token)).thenReturn(claims);

return token;
}
}
Loading

0 comments on commit 9ec71e3

Please sign in to comment.