Skip to content

Commit

Permalink
Merge pull request #82 from oduck-team/feature/80
Browse files Browse the repository at this point in the history
애니메이션 별점 평가 여부 조회 구현 #80
  • Loading branch information
FaberJoo authored Nov 7, 2023
2 parents 231b592 + ee2aba9 commit 74f909b
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 6 deletions.
40 changes: 35 additions & 5 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,6 @@ include::{snippets}/deleteAnime/success/http-request.adoc[]
.request-param
include::{snippets}/deleteAnime/success/path-parameters.adoc[]

.request-fields
include::{snippets}/deleteAnime/success/request-fields.adoc[]

==== 성공시

.http-response
Expand Down Expand Up @@ -1039,7 +1036,7 @@ include::{snippets}/deleteSeries/success/http-response.adoc[]
==== 실패시

== starRating
=== POST api/v1/ratings
=== POST api/v1/ratings/:animeId
.curl-request
include::{snippets}/starRating/createScore/success/curl-request.adoc[]

Expand All @@ -1064,4 +1061,37 @@ include::{snippets}/starRating/createScore/success/http-response.adoc[]

==== 실패시
.http-response
include::{snippets}/starRating/createScore/failure/http-response.adoc[]
include::{snippets}/starRating/createScore/failure/http-response.adoc[]

=== GET api/v1/ratings/:animeId
.curl-request
include::{snippets}/starRating/checkRated/exist/curl-request.adoc[]

.http-request
include::{snippets}/starRating/checkRated/exist/http-request.adoc[]

.path-parameters
include::{snippets}/starRating/checkRated/exist/path-parameters.adoc[]

.request-header
include::{snippets}/starRating/checkRated/exist/request-headers.adoc[]

==== 성공시
.http-response
include::{snippets}/starRating/checkRated/exist/http-response.adoc[]

.response-body
include::{snippets}/starRating/checkRated/exist/response-body.adoc[]

.response-fields
include::{snippets}/starRating/checkRated/exist/response-fields.adoc[]

==== 실패시
.http-response
include::{snippets}/starRating/checkRated/notExist/http-response.adoc[]

.response-body
include::{snippets}/starRating/checkRated/notExist/response-body.adoc[]

.response-fields
include::{snippets}/starRating/checkRated/notExist/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -33,4 +34,11 @@ public ResponseEntity<?> PostScore(
return ResponseEntity.status(res ? HttpStatus.CREATED : HttpStatus.CONFLICT).build();
}

@GetMapping("/{animeId}")
public ResponseEntity<?> GetScore(
@PathVariable("animeId") @Positive Long animeId,
@LoginUser AuthUser user
) {
return ResponseEntity.ok(starRatingService.checkRated(user.getId(), animeId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.oduck.api.domain.starRating.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

public class StarRatingResDto {
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class RatedDateTimeRes {
private String createdAt;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

@Entity
@Getter
Expand All @@ -37,6 +39,10 @@ public class StarRating extends BaseEntity {
@Column(nullable = false)
private int score;

@CreationTimestamp
@Column(nullable = false, updatable = false)
protected LocalDateTime createdAt;

public void relateMember(Member member) {
this.member = member;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.oduck.api.domain.starRating.service;

import io.oduck.api.domain.starRating.dto.StarRatingResDto.RatedDateTimeRes;

public interface StarRatingService {
boolean createScore(Long memberId, Long animeId, int score);
RatedDateTimeRes checkRated(Long memberId, Long animeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.oduck.api.domain.anime.repository.AnimeRepository;
import io.oduck.api.domain.member.entity.Member;
import io.oduck.api.domain.member.repository.MemberRepository;
import io.oduck.api.domain.starRating.dto.StarRatingResDto.RatedDateTimeRes;
import io.oduck.api.domain.starRating.entity.StarRating;
import io.oduck.api.domain.starRating.repository.StarRatingRepository;
import io.oduck.api.global.exception.NotFoundException;
Expand All @@ -24,7 +25,7 @@ public class StarRatingServiceImpl implements StarRatingService {
@Override
@Transactional
public boolean createScore(Long memberId, Long animeId, int score) {
Optional<StarRating> foundStarRating = starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId);
Optional<StarRating> foundStarRating = findByMemberIdAndAnimeId(memberId, animeId);
if (foundStarRating.isPresent()) {
return false;
}
Expand All @@ -46,4 +47,18 @@ public boolean createScore(Long memberId, Long animeId, int score) {
starRatingRepository.save(starRating);
return true;
}

@Override
public RatedDateTimeRes checkRated(Long memberId, Long animeId) {
StarRating foundStarRating = findByMemberIdAndAnimeId(memberId, animeId)
.orElseThrow(() -> new NotFoundException("StarRating"));

return RatedDateTimeRes.builder()
.createdAt(foundStarRating.getCreatedAt().toString())
.build();
}

private Optional<StarRating> findByMemberIdAndAnimeId(Long memberId, Long animeId) {
return starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
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.snippet.Attributes.attributes;
Expand Down Expand Up @@ -156,4 +158,95 @@ void createScoreIfAlreadyExist() throws Exception {
);
}
}

@Nested
@DisplayName("GET /ratings/{animeId}")
class GetScore {
@Test
@DisplayName("평점이 없을 때 평점을 조회하면 200 OK를 반환")
@WithCustomMockMember(id = 1L, email = "admin", password = "Qwer!234", role = Role.MEMBER)
void checkRatedExist() throws Exception {
// given
Long animeId = 1L;

// when
ResultActions actions = mockMvc.perform(
get(BASE_URL + "/{animeId}", animeId)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}")
);

// then
actions
.andExpect(status().isOk())
.andDo(
document("starRating/checkRated/exist",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("animeId")
.description("애니메 id")
),
requestHeaders(
headerWithName(HttpHeaders.COOKIE)
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.description("Header Cookie, 세션 쿠키")
),
responseFields(
attributes(key("title")
.value("Fields for starRating get")),
fieldWithPath("createdAt")
.description("별점 생성일")
)
)
);
}

@Test
@DisplayName("평점이 없을 때 평점을 조회하면 404 Not Found를 반환")
@WithCustomMockMember(id = 3L, email = "david", password = "Qwer!234", role = Role.MEMBER)
void checkRatedNotExist() throws Exception {
// given
Long animeId = 2L;

// when
ResultActions actions = mockMvc.perform(
get(BASE_URL + "/{animeId}", animeId)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}")
);

// then
actions
.andExpect(status().isNotFound())
.andDo(
document("starRating/checkRated/notExist",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("animeId")
.description("애니메 id")
),
requestHeaders(
headerWithName(HttpHeaders.COOKIE)
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.description("Header Cookie, 세션 쿠키")
),
responseFields(
attributes(key("title")
.value("Fields for bookmark creation")),
fieldWithPath("message")
.description("에러 메시지"),
fieldWithPath("fieldErrors")
.description("필드 에러 목록"),
fieldWithPath("violationErrors")
.description("위반 에러 목록")
)
)
);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.BDDMockito.given;

import io.oduck.api.domain.anime.entity.Anime;
import io.oduck.api.domain.anime.repository.AnimeRepository;
import io.oduck.api.domain.member.entity.Member;
import io.oduck.api.domain.member.repository.MemberRepository;
import io.oduck.api.domain.starRating.dto.StarRatingResDto.RatedDateTimeRes;
import io.oduck.api.domain.starRating.entity.StarRating;
import io.oduck.api.domain.starRating.repository.StarRatingRepository;
import io.oduck.api.domain.starRating.service.StarRatingServiceImpl;
import io.oduck.api.global.exception.NotFoundException;
import java.time.LocalDateTime;
import java.util.Optional;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -90,4 +95,56 @@ void createScoreIfAlreadyExist() {
assertFalse(result);
}
}

@Nested
@DisplayName("별점 조회")
class GetScore {
Long memberId = 1L;
Long animeId = 1L;
int score = 5;

Member member = Member.builder()
.id(memberId)
.build();
Anime anime = Anime.builder()
.id(animeId)
.build();

@Test
@DisplayName("별점 조회 성공")
void chekRated() {
// given
StarRating starRating = StarRating.builder()
.member(member)
.anime(anime)
.score(score)
.createdAt(LocalDateTime.now())
.build();

given(starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId))
.willReturn(Optional.ofNullable(starRating));

// when
StarRating foundStarRating = starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId)
.orElseThrow(() -> new RuntimeException("StarRating"));

RatedDateTimeRes createdAtScore = starRatingService.checkRated(memberId, animeId);

// then
assertDoesNotThrow(() -> starRatingService.checkRated(memberId, animeId));
assertNotNull(createdAtScore);
}

@DisplayName("별점 조회 실패")
@Test
void chekRatedIfNotExist() {
// given
given(starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId))
.willReturn(Optional.empty());

// when
// then
assertThrows(NotFoundException.class, () -> starRatingService.checkRated(memberId, animeId));
}
}
}

0 comments on commit 74f909b

Please sign in to comment.