Skip to content

Commit

Permalink
Merge pull request #94 from oduck-team/feature/90
Browse files Browse the repository at this point in the history
애니메이션 별점 평가 수정 구현 #90
  • Loading branch information
FaberJoo authored Nov 9, 2023
2 parents 54cfd47 + 7e49f95 commit 6bc1ae2
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 6 deletions.
27 changes: 27 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1367,3 +1367,30 @@ include::{snippets}/starRating/checkRated/notExist/response-body.adoc[]

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

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

.http-request
include::{snippets}/starRating/patchScore/success/http-request.adoc[]

.path-parameters
include::{snippets}/starRating/patchScore/success/path-parameters.adoc[]

.request-header
include::{snippets}/starRating/patchScore/success/request-headers.adoc[]

.request-body
include::{snippets}/starRating/patchScore/success/request-body.adoc[]

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

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

==== 실패시
.http-response
include::{snippets}/starRating/patchScore/failure/http-response.adoc[]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.oduck.api.domain.starRating.controller;

import io.oduck.api.domain.starRating.dto.StarRatingReqDto.CreateReq;
import io.oduck.api.domain.starRating.dto.StarRatingReqDto.CreateAndPatchReq;
import io.oduck.api.domain.starRating.service.StarRatingService;
import io.oduck.api.global.security.auth.dto.AuthUser;
import io.oduck.api.global.security.auth.dto.LoginUser;
Expand All @@ -11,6 +11,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -27,7 +28,7 @@ public class StarRatingController {
@PostMapping("/{animeId}")
public ResponseEntity<?> PostScore(
@PathVariable("animeId") @Positive Long animeId,
@RequestBody @Valid CreateReq body,
@RequestBody @Valid CreateAndPatchReq body,
@LoginUser AuthUser user
) {
boolean res = starRatingService.createScore(user.getId(), animeId, body.getScore());
Expand All @@ -41,4 +42,14 @@ public ResponseEntity<?> GetScore(
) {
return ResponseEntity.ok(starRatingService.checkRated(user.getId(), animeId));
}

@PatchMapping("/{animeId}")
public ResponseEntity<?> PatchScore(
@PathVariable("animeId") @Positive Long animeId,
@RequestBody @Valid CreateAndPatchReq body,
@LoginUser AuthUser user
) {
boolean res = starRatingService.updateScore(user.getId(), animeId, body.getScore());
return ResponseEntity.status(res ? HttpStatus.NO_CONTENT : HttpStatus.CONFLICT).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class StarRatingReqDto {
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class CreateReq {
public static class CreateAndPatchReq {
@Min(value = 1, message = "score must be greater than or equal to 1")
@Max(value = 10, message = "score must be less than or equal to 10")
private int score;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ public void relateMember(Member member) {
public void relateAnime(Anime anime) {
this.anime = anime;
}

public void updateScore(int score) {
this.score = score;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
public interface StarRatingService {
boolean createScore(Long memberId, Long animeId, int score);
RatedDateTimeRes checkRated(Long memberId, Long animeId);
boolean updateScore(Long memberId, Long animeId, int score);
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ public RatedDateTimeRes checkRated(Long memberId, Long animeId) {
.build();
}

@Override
public boolean updateScore(Long memberId, Long animeId, int score) {
StarRating foundStarRating = findByMemberIdAndAnimeId(memberId, animeId)
.orElseThrow(() -> new NotFoundException("StarRating"));

if (foundStarRating.getScore() == score) {
return false;
}

foundStarRating.updateScore(score);

starRatingRepository.save(foundStarRating);
return true;
}

private Optional<StarRating> findByMemberIdAndAnimeId(Long memberId, Long animeId) {
return starRatingRepository.findByMemberIdAndAnimeId(memberId, animeId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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.patch;
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;
Expand All @@ -20,7 +21,7 @@

import com.google.gson.Gson;
import io.oduck.api.domain.member.entity.Role;
import io.oduck.api.domain.starRating.dto.StarRatingReqDto.CreateReq;
import io.oduck.api.domain.starRating.dto.StarRatingReqDto.CreateAndPatchReq;
import io.oduck.api.global.mockMember.WithCustomMockMember;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -64,7 +65,7 @@ void createScore() throws Exception {
Long animeId = 2L;
int score = 5;

CreateReq body = CreateReq.builder()
CreateAndPatchReq body = CreateAndPatchReq.builder()
.score(score)
.build();

Expand Down Expand Up @@ -115,7 +116,7 @@ void createScoreIfAlreadyExist() throws Exception {
Long animeId = 1L;
int score = 5;

CreateReq body = CreateReq.builder()
CreateAndPatchReq body = CreateAndPatchReq.builder()
.score(score)
.build();

Expand Down Expand Up @@ -247,6 +248,111 @@ void checkRatedNotExist() throws Exception {
)
);
}
}

@Nested
@DisplayName("PATCH /ratings/{animeId}")
class PatchScore {
@Test
@DisplayName("평점 수정 성공시 204 No Content 반환")
@WithCustomMockMember(id = 2L, email = "john", password = "Qwer!234", role = Role.MEMBER)
void patchScore() throws Exception {
// given
Long animeId = 1L;
int score = 5;

CreateAndPatchReq body = CreateAndPatchReq.builder()
.score(score)
.build();

String content = gson.toJson(body);

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

// then
actions
.andExpect(status().isNoContent())
.andDo(document("starRating/patchScore/success",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("animeId")
.description("애니메이션 식별자")
),
requestHeaders(
headerWithName(HttpHeaders.COOKIE)
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.optional()
.description("Header Cookie, 세션 쿠키")
),
requestFields(
attributes(key("title")
.value("Fields for starRating creation")),
fieldWithPath("score")
.type(JsonFieldType.NUMBER)
.attributes(field("constraints", "1~10 사이의 정수"))
.description("애니메 별점")
)
)
);
}

@Test
@DisplayName("기존 평점과 동일하다면 없다면 409 Conflict 반환")
@WithCustomMockMember(id = 3L, email = "john", password = "Qwer!234", role = Role.MEMBER)
void patchScoreIfNotExist() throws Exception {
// given
Long animeId = 1L;
int score = 3;

CreateAndPatchReq body = CreateAndPatchReq.builder()
.score(score)
.build();

String content = gson.toJson(body);

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

// then
actions
.andExpect(status().isConflict())
.andDo(document("starRating/patchScore/failure",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(
parameterWithName("animeId")
.description("애니메이션 식별자")
),
requestHeaders(
headerWithName(HttpHeaders.COOKIE)
.attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}"))
.optional()
.description("Header Cookie, 세션 쿠키")
),
requestFields(
attributes(key("title")
.value("Fields for starRating creation")),
fieldWithPath("score")
.type(JsonFieldType.NUMBER)
.attributes(field("constraints", "1~10 사이의 정수"))
.description("애니메 별점")
)
)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,61 @@ void chekRatedIfNotExist() {
assertThrows(NotFoundException.class, () -> starRatingService.checkRated(memberId, animeId));
}
}

@Nested
@DisplayName("별점 수정")
class UpdateScore {
Long memberId = 1L;
Long animeId = 1L;
int score = 2;

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

@Test
@DisplayName("별점 수정 성공")
void updateScore() {
// given
StarRating starRating = StarRating.builder()
.member(member)
.anime(anime)
.score(score)
.build();

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

// when
boolean result = starRatingService.updateScore(memberId, animeId, 5);

// then
assertDoesNotThrow(() -> starRatingService.updateScore(memberId, animeId, score));
assertTrue(result);
}

@Test
@DisplayName("별점 수정 실패")
void updateScoreIfNotExist() {
// given
StarRating starRating = StarRating.builder()
.member(member)
.anime(anime)
.score(score)
.build();

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

// when
boolean result = starRatingService.updateScore(memberId, animeId, score);

// then
assertDoesNotThrow(() -> starRatingService.updateScore(memberId, animeId, score));
assertFalse(result);
}
}
}

0 comments on commit 6bc1ae2

Please sign in to comment.