diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 170fdf2c..499b35b9 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -740,6 +740,33 @@ include::{snippets}/postShortReview/success/http-response.adoc[] ==== 실패시 +=== shortReview/getAttractionPoint api/v1/short-reviews//attraction-points + +.curl-request +include::{snippets}/shortReview/getAttractionPoints/success/curl-request.adoc[] + +.http-request +include::{snippets}/shortReview/getAttractionPoints/success/http-request.adoc[] + +.httpie-request +include::{snippets}/shortReview/getAttractionPoints/success/httpie-request.adoc[] + +.request-headers +include::{snippets}/shortReview/getAttractionPoints/success/request-headers.adoc[] + +.response-body +include::{snippets}/shortReview/getAttractionPoints/success/response-body.adoc[] + +.query-parameters +include::{snippets}/shortReview/getAttractionPoints/success/query-parameters.adoc[] + +==== 성공시 +.http-response +include::{snippets}/shortReview/getAttractionPoints/success/http-response.adoc[] + +.response-fields +include::{snippets}/shortReview/getAttractionPoints/success/response-fields.adoc[] + === PATCH api/v1/short-reviews/1 .curl-request @@ -761,9 +788,6 @@ include::{snippets}/patchShortReview/success/request-fields.adoc[] .http-response include::{snippets}/patchShortReview/success/http-response.adoc[] -.response-body -include::{snippets}/patchShortReview/success/response-body.adoc[] - .response-fields include::{snippets}/patchShortReview/success/response-fields.adoc[] diff --git a/src/main/java/io/oduck/api/domain/review/controller/ShortReviewController.java b/src/main/java/io/oduck/api/domain/review/controller/ShortReviewController.java index 10da33d2..6a4fb6a2 100644 --- a/src/main/java/io/oduck/api/domain/review/controller/ShortReviewController.java +++ b/src/main/java/io/oduck/api/domain/review/controller/ShortReviewController.java @@ -37,13 +37,12 @@ public class ShortReviewController { @PostMapping public ResponseEntity postShortReview( @LoginUser AuthUser user, - @RequestBody @Valid ShortReviewReqDto.PostShortReviewReq req) { + @RequestBody @Valid ShortReviewReqDto.ShortReviewReq req) { //TODO : 짧은 리뷰 작성 - shortReviewService.save(user.getId() == null? 0L: user.getId(), req); return ResponseEntity.ok().build(); } - @GetMapping("/{animeId}") + @GetMapping("/animes/{animeId}") public ResponseEntity getShortReviews( @PathVariable Long animeId, @RequestParam(required = false) String cursor, @@ -55,15 +54,23 @@ public ResponseEntity getShortReviews( SliceResponse reviewRes = shortReviewService.getShortReviews(animeId, cursor, sort, order, size); return ResponseEntity.ok(reviewRes); } - @PatchMapping("/{reviewId}") + @GetMapping("/attraction-points") + public ResponseEntity patchShortReview( + @LoginUser AuthUser user, + @RequestParam(required = false) Long animeId, + @RequestParam(required = false) String name) { + //TODO : 짧은 리뷰 수정, 입덕포인트 반환 + IsAttractionPoint attractionPointRes = attractionPointService.isAttractionPoint(user.getId(), animeId); + return ResponseEntity.ok(attractionPointRes); + } + + @PatchMapping("/{reviewId}") public ResponseEntity patchShortReview( @LoginUser AuthUser user, @PathVariable Long reviewId, - @RequestBody @Valid ShortReviewReqDto.PatchShortReviewReq req) { + @RequestBody @Valid ShortReviewReqDto.ShortReviewReq req) { //TODO : 짧은 리뷰 수정 shortReviewService.update(user.getId(), reviewId, req); - //입덕포인트 반환 - IsAttractionPoint attractionPointRes = attractionPointService.isAttractionPoint(user.getId(), req.getAnimeId()); - return ResponseEntity.ok(attractionPointRes); + return ResponseEntity.noContent().build(); } } \ No newline at end of file diff --git a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewDslDto.java b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewDslDto.java index e8075569..266a6305 100644 --- a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewDslDto.java +++ b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewDslDto.java @@ -10,24 +10,26 @@ public class ShortReviewDslDto { @Getter @NoArgsConstructor public static class ShortReviewDsl{ + private Long reviewId; private Long animeId; private String name; private String thumbnail; private Integer score; private String content; - private boolean hasSpoiler; + private Boolean isSpoiler; private Long likeCount; private LocalDateTime createdAt; @Builder - public ShortReviewDsl(Long animeId, String name, String thumbnail, Integer score, String content, - boolean hasSpoiler, Long likeCount, LocalDateTime createdAt) { + public ShortReviewDsl(Long reviewId, Long animeId, String name, String thumbnail, Integer score, String content, + Boolean isSpoiler, Long likeCount, LocalDateTime createdAt) { + this.reviewId = reviewId; this.animeId = animeId; this.name = name; this.thumbnail = thumbnail; this.score = score; this.content = content; - this.hasSpoiler = hasSpoiler; + this.isSpoiler = isSpoiler; this.likeCount = likeCount; this.createdAt = createdAt; } diff --git a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewReqDto.java b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewReqDto.java index 4371b4b3..96012931 100644 --- a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewReqDto.java +++ b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewReqDto.java @@ -13,21 +13,7 @@ public class ShortReviewReqDto { @NoArgsConstructor @AllArgsConstructor @Builder - public static class PostShortReviewReq{ - private Long animeId; - private String name; - private boolean hasSpoiler; - @NotBlank - @Length(min = 10, max = 100, - message = "최소 10에서 100자 까지 입력 가능합니다.") - private String content; - } - - @Getter - @NoArgsConstructor - @AllArgsConstructor - @Builder - public static class PatchShortReviewReq{ + public static class ShortReviewReq{ private Long animeId; private String name; private boolean hasSpoiler; @@ -42,7 +28,7 @@ public static class PatchShortReviewReq{ public enum Sort{ //좋아요순(기본), 최신순, 평점 높은순, 평점 낮은순으로 조회 가능 CREATED_AT("createdAt"), - LIKE("likeCount"), + LIKE_COUNT("likeCount"), SCORE("score"); private final String sort; diff --git a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewResDto.java b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewResDto.java index fda1872f..acdca1bb 100644 --- a/src/main/java/io/oduck/api/domain/review/dto/ShortReviewResDto.java +++ b/src/main/java/io/oduck/api/domain/review/dto/ShortReviewResDto.java @@ -15,13 +15,14 @@ public class ShortReviewResDto {; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor public static class ShortReviewRes implements EntityBased { + private Long reviewId; private Long animeId; private String name; private String thumbnail; private Integer score; private String content; - private boolean hasSpoiler; - private boolean hasLike; + private Boolean isSpoiler; + private Boolean isLike; @Builder.Default private Long likeCount = 0L; private LocalDateTime createdAt; @@ -29,17 +30,18 @@ public static class ShortReviewRes implements EntityBased { //카운트가 없으면 hasLike false @Builder public static ShortReviewRes of(ShortReviewDsl shortReviewDsl){ - boolean hasLike = shortReviewDsl.getLikeCount() != null; - Long likeCount = hasLike ? shortReviewDsl.getLikeCount() : 0L ; + Boolean isLike = shortReviewDsl.getLikeCount() != null; + Long likeCount = isLike ? shortReviewDsl.getLikeCount() : 0L ; return ShortReviewRes .builder() + .reviewId(shortReviewDsl.getReviewId()) .animeId(shortReviewDsl.getAnimeId()) .name(shortReviewDsl.getName()) .thumbnail(shortReviewDsl.getThumbnail()) .score(shortReviewDsl.getScore()) .content(shortReviewDsl.getContent()) - .hasSpoiler(shortReviewDsl.isHasSpoiler()) - .hasLike(hasLike) + .isSpoiler(shortReviewDsl.getIsSpoiler()) + .isLike(isLike) .likeCount(likeCount) .createdAt(shortReviewDsl.getCreatedAt()) .build(); diff --git a/src/main/java/io/oduck/api/domain/review/repository/ShortReviewRepositoryImpl.java b/src/main/java/io/oduck/api/domain/review/repository/ShortReviewRepositoryImpl.java index 24f58624..7c618a9e 100644 --- a/src/main/java/io/oduck/api/domain/review/repository/ShortReviewRepositoryImpl.java +++ b/src/main/java/io/oduck/api/domain/review/repository/ShortReviewRepositoryImpl.java @@ -5,6 +5,7 @@ import static io.oduck.api.domain.review.entity.QShortReview.shortReview; import static io.oduck.api.domain.reviewLike.entity.QShortReviewLike.shortReviewLike; import static io.oduck.api.domain.starRating.entity.QStarRating.starRating; + import static io.oduck.api.global.utils.QueryDslUtils.fetchSliceByCursor; import com.querydsl.core.types.Path; @@ -30,15 +31,17 @@ public class ShortReviewRepositoryImpl implements ShortReviewRepositoryCustom{ private final JPAQueryFactory query; + + @Override public Slice selectShortReviews(Long animeId, String cursor, Pageable pageable) { String property = pageable.getSort().get().toList().get(0).getProperty(); - JPAQuery shortReviews = query .select( Projections.constructor( ShortReviewDsl.class, + shortReview.id, anime.id, member.memberProfile.name, member.memberProfile.thumbnail, @@ -52,13 +55,14 @@ public Slice selectShortReviews(Long animeId, String cursor, .from(shortReview) .join(member).on(member.id.eq(shortReview.member.id)) .join(anime).on(anime.id.eq(shortReview.anime.id)) - .join(shortReviewLike).on(shortReviewLike.shortReview.id.eq(shortReview.id)) - .leftJoin(starRating).on(starRating.anime.id.eq(anime.id).and(starRating.member.id.eq(member.id))) + .leftJoin(shortReviewLike).on(shortReview.id.eq(shortReviewLike.shortReview.id)) + .join(starRating).on(starRating.anime.id.eq(shortReview.anime.id).and(starRating.member.id.eq(shortReview.member.id))) .where(anime.id.eq(animeId)) - .where(cursorCondition(cursor, pageable)) + .groupBy(shortReview.id, member.id) + .having(cursorCondition(cursor, pageable)) .limit(pageable.getPageSize()); - return fetchSliceByCursor(sortPath(property),shortReviews, pageable); + return fetchSliceByCursor(sortPath(property), shortReviews, pageable ); } private BooleanExpression cursorCondition(String cursor, Pageable pageable){ @@ -69,6 +73,7 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){ String property = orderList.get(0).getProperty(); switch (property){ + case "likeCount": String[] likeCountAndCreateAt = cursor.split(", "); int likeCount = Integer.parseInt(likeCountAndCreateAt[0]); @@ -77,11 +82,11 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){ if(direction == Direction.ASC) { return shortReviewLike.id.count().gt(likeCount) .or(shortReviewLike.id.count().goe(likeCount).and(shortReview.createdAt.lt(likeCountCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면 - .or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(likeCountCreateAt))); + .or(shortReviewLike.id.count().isNull().and(shortReview.createdAt.lt(likeCountCreateAt))); } else{ return shortReviewLike.id.count().lt(likeCount) .or(shortReviewLike.id.count().loe(likeCount).and(shortReview.createdAt.lt(likeCountCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면 - .or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(likeCountCreateAt))); + .or(shortReviewLike.id.count().isNull().and(shortReview.createdAt.lt(likeCountCreateAt))); } case "score": @@ -91,12 +96,10 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){ if(direction == Direction.ASC) { return starRating.score.gt(score) - .or(starRating.score.goe(score).and(shortReview.createdAt.lt(scoreCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면 - .or(starRating.score.isNotNull().and(shortReview.createdAt.lt(scoreCreateAt))); + .or(starRating.score.goe(score).and(shortReview.createdAt.lt(scoreCreateAt))); } else{ - return shortReviewLike.id.count().lt(score) - .or(shortReviewLike.id.count().loe(score).and(shortReview.createdAt.lt(scoreCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면 - .or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(scoreCreateAt))); + return starRating.score.lt(score) + .or(starRating.score.loe(score).and(shortReview.createdAt.lt(scoreCreateAt))); } default: if (direction == Direction.ASC) { diff --git a/src/main/java/io/oduck/api/domain/review/service/ShortReviewService.java b/src/main/java/io/oduck/api/domain/review/service/ShortReviewService.java index 0f53d71e..5a5aa7c0 100644 --- a/src/main/java/io/oduck/api/domain/review/service/ShortReviewService.java +++ b/src/main/java/io/oduck/api/domain/review/service/ShortReviewService.java @@ -1,7 +1,6 @@ package io.oduck.api.domain.review.service; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq; +import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq; import io.oduck.api.domain.review.dto.ShortReviewReqDto.Sort; import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewCountRes; import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewRes; @@ -11,14 +10,14 @@ public interface ShortReviewService { //애니 리뷰 작성 - void save(Long memberId, PostShortReviewReq shortReviewReq); + void save(Long memberId, ShortReviewReq shortReviewReq); //애니 짧은 리뷰 조회 SliceResponse getShortReviews(Long animeId, String cursor, Sort sort, OrderDirection order, int size); ShortReviewCountRes getShortReviewCountByMemberId(Long memberId); //애니 리뷰 수정 - void update(Long memberId, Long reviewId, PatchShortReviewReq req); + void update(Long memberId, Long reviewId, ShortReviewReq req); //애니 리뷰 삭제 diff --git a/src/main/java/io/oduck/api/domain/review/service/ShortReviewServiceImpl.java b/src/main/java/io/oduck/api/domain/review/service/ShortReviewServiceImpl.java index 47c72870..01715eba 100644 --- a/src/main/java/io/oduck/api/domain/review/service/ShortReviewServiceImpl.java +++ b/src/main/java/io/oduck/api/domain/review/service/ShortReviewServiceImpl.java @@ -5,12 +5,11 @@ 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.MemberProfileRepository; import io.oduck.api.domain.member.repository.MemberRepository; import io.oduck.api.domain.review.dto.ShortReviewDslDto.ShortReviewDsl; import io.oduck.api.domain.review.dto.ShortReviewReqDto; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq; +import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq; + import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewCountRes; import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewRes; import io.oduck.api.domain.review.entity.ShortReview; @@ -35,15 +34,13 @@ public class ShortReviewServiceImpl implements ShortReviewService{ private final ShortReviewRepository shortReviewRepository; - private final MemberProfileRepository memberProfileRepository; private final MemberRepository memberRepository; - private final AnimeRepository animeRepository; @Override @Transactional - public void save(Long memberId, PostShortReviewReq shortReviewReq) { + public void save(Long memberId, ShortReviewReq shortReviewReq) { ShortReview shortReview = ShortReview .builder() .content(shortReviewReq.getContent()) @@ -60,7 +57,7 @@ public void save(Long memberId, PostShortReviewReq shortReviewReq) { //회원 입력 Member member = memberRepository.findById(memberId) .orElseThrow( - () -> new NotFoundException("Memebr") + () -> new NotFoundException("Member") ); shortReview.relateMember(member); @@ -77,7 +74,7 @@ public SliceResponse getShortReviews(Long animeId, String cursor sort.getSort() ); - if(sort == ShortReviewReqDto.Sort.LIKE){ + if(sort == ShortReviewReqDto.Sort.LIKE_COUNT){ sortList = sortList.and(Sort.by(Direction.DESC, "createdAt")); }else if(sort == ShortReviewReqDto.Sort.SCORE){ sortList = sortList.and(Sort.by(Direction.DESC, "createdAt")); @@ -107,9 +104,8 @@ public ShortReviewCountRes getShortReviewCountByMemberId(Long memberId) { .count(count) .build(); } - @Override - public void update(Long memberId, Long reviewId, PatchShortReviewReq req) { + public void update(Long memberId, Long reviewId, ShortReviewReq req) { ShortReview findShortReview = getShortReview(reviewId); Long findMemberId = findShortReview.getMember().getId(); //리뷰 작성자 인지 확인 diff --git a/src/main/java/io/oduck/api/global/utils/QueryDslUtils.java b/src/main/java/io/oduck/api/global/utils/QueryDslUtils.java index 2cff6bff..35d3c9d0 100644 --- a/src/main/java/io/oduck/api/global/utils/QueryDslUtils.java +++ b/src/main/java/io/oduck/api/global/utils/QueryDslUtils.java @@ -4,6 +4,7 @@ import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Path; import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.NumberPath; import com.querydsl.jpa.impl.JPAQuery; import java.util.ArrayList; import java.util.Iterator; @@ -66,6 +67,12 @@ private static List convertToDslOrder(Sort sort, List path } private static OrderSpecifier createOrderSpecifier(Order order, Path parent, String fieldName) { + // count 메소드 컬럼을 기준으로 할 때의 OrderSpecifier + if (fieldName.equals("quantity") || fieldName.equals("likeCount")) { + NumberPath aliasQuantity = Expressions.numberPath(Long.class, fieldName); + + return new OrderSpecifier(order, aliasQuantity); + } // 일반 컬럼을 기준으로 할때의 OrderSpecifier Path fieldPath = Expressions.path(Object.class, parent, fieldName); diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 93eb2b2d..803c8788 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -28,6 +28,7 @@ spring: password: sql: init: + encoding: utf-8 mode: always data-locations: classpath:db/data.sql security: diff --git a/src/test/java/io/oduck/api/e2e/shortReview/ShortReviewControllerTest.java b/src/test/java/io/oduck/api/e2e/shortReview/ShortReviewControllerTest.java index 5ad18748..e6e50461 100644 --- a/src/test/java/io/oduck/api/e2e/shortReview/ShortReviewControllerTest.java +++ b/src/test/java/io/oduck/api/e2e/shortReview/ShortReviewControllerTest.java @@ -22,8 +22,7 @@ import com.google.gson.Gson; import io.oduck.api.domain.member.entity.Role; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq; +import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq; import io.oduck.api.global.mockMember.WithCustomMockMember; import io.oduck.api.global.utils.ShortReviewTestUtils; import org.junit.jupiter.api.DisplayName; @@ -67,7 +66,7 @@ class PostShortReviews{ @WithCustomMockMember(id = 2L, email = "john", password = "Qwer!234", role = Role.MEMBER) void postShortReview() throws Exception{ //given - PostShortReviewReq req = ShortReviewTestUtils.createPostShoreReviewReq(); + ShortReviewReq req = ShortReviewTestUtils.createPostShoreReviewReq(); String content = gson.toJson(req); //when @@ -84,11 +83,12 @@ void postShortReview() throws Exception{ .andDo(document("postShortReview/success", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - requestHeaders( - headerWithName(HttpHeaders.COOKIE) - .attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}")) - .description("Header Cookie, 세션 쿠키") - ), + requestHeaders( + headerWithName(HttpHeaders.COOKIE) + .attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}")) + .optional() + .description("Header Cookie, 세션 쿠키") + ), requestFields(attributes(key("title").value("Fields for shortReview creation")), fieldWithPath("animeId") .type(JsonFieldType.NUMBER) @@ -122,12 +122,12 @@ void getShortReviews() throws Exception{ //given Long animeId = 1L; int size = 2; - String sort = "created_at"; + String sort = "like_count"; String order = "desc"; //when ResultActions actions = mockMvc.perform( - get(BASE_URL+ "/{animeId}", animeId) + get(BASE_URL+ "/animes/{animeId}", animeId) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .param("size", String.valueOf(size)) @@ -139,13 +139,14 @@ void getShortReviews() throws Exception{ actions .andExpect(status().isOk()) .andExpect(jsonPath("$.items").isArray()) + .andExpect(jsonPath("$.items[0].reviewId").exists()) .andExpect(jsonPath("$.items[0].animeId").exists()) .andExpect(jsonPath("$.items[0].name").exists()) .andExpect(jsonPath("$.items[0].thumbnail").exists()) .andExpect(jsonPath("$.items[0].score").exists()) .andExpect(jsonPath("$.items[0].content").exists()) - .andExpect(jsonPath("$.items[0].hasSpoiler").exists()) - .andExpect(jsonPath("$.items[0].hasLike").exists()) + .andExpect(jsonPath("$.items[0].isSpoiler").exists()) + .andExpect(jsonPath("$.items[0].isLike").exists()) .andExpect(jsonPath("$.items[0].likeCount").exists()) .andExpect(jsonPath("$.items[0].createdAt").exists()) .andExpect(jsonPath("$.size").exists()) @@ -179,6 +180,9 @@ void getShortReviews() throws Exception{ fieldWithPath("items") .type(JsonFieldType.ARRAY) .description("조회 데이터"), + fieldWithPath("items[].reviewId") + .type(JsonFieldType.NUMBER) + .description("리뷰 고유 식별자"), fieldWithPath("items[].animeId") .type(JsonFieldType.NUMBER) .description("애니 고유 식별자"), @@ -194,10 +198,10 @@ void getShortReviews() throws Exception{ fieldWithPath("items[].content") .type(JsonFieldType.STRING) .description("짧은 리뷰 내용"), - fieldWithPath("items[].hasSpoiler") + fieldWithPath("items[].isSpoiler") .type(JsonFieldType.BOOLEAN) .description("스포일러 유무"), - fieldWithPath("items[].hasLike") + fieldWithPath("items[].isLike") .type(JsonFieldType.BOOLEAN) .description("짧은 리뷰 좋아요 유무"), fieldWithPath("items[].likeCount") @@ -226,14 +230,14 @@ void getShortReviews() throws Exception{ void getShortReviewsWithCursor() throws Exception{ //given Long animeId = 1L; - int size = 1; - String sort = "created_at"; + int size = 2; + String sort = "like_count"; String order = "desc"; - String cursor = "2023-10-11T21:05:31.859"; + String cursor = "0, 2023-10-11T21:05:31.859"; //when ResultActions actions = mockMvc.perform( - get(BASE_URL+ "/{animeId}", animeId) + get(BASE_URL+ "/animes/{animeId}", animeId) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .param("size", String.valueOf(size)) @@ -246,13 +250,14 @@ void getShortReviewsWithCursor() throws Exception{ actions .andExpect(status().isOk()) .andExpect(jsonPath("$.items").isArray()) + .andExpect(jsonPath("$.items[0].reviewId").exists()) .andExpect(jsonPath("$.items[0].animeId").exists()) .andExpect(jsonPath("$.items[0].name").exists()) .andExpect(jsonPath("$.items[0].thumbnail").exists()) .andExpect(jsonPath("$.items[0].score").exists()) .andExpect(jsonPath("$.items[0].content").exists()) - .andExpect(jsonPath("$.items[0].hasSpoiler").exists()) - .andExpect(jsonPath("$.items[0].hasLike").exists()) + .andExpect(jsonPath("$.items[0].isSpoiler").exists()) + .andExpect(jsonPath("$.items[0].isLike").exists()) .andExpect(jsonPath("$.items[0].likeCount").exists()) .andExpect(jsonPath("$.items[0].createdAt").exists()) .andExpect(jsonPath("$.size").exists()) @@ -286,6 +291,9 @@ void getShortReviewsWithCursor() throws Exception{ fieldWithPath("items") .type(JsonFieldType.ARRAY) .description("조회 데이터"), + fieldWithPath("items[].reviewId") + .type(JsonFieldType.NUMBER) + .description("리뷰 고유 식별자"), fieldWithPath("items[].animeId") .type(JsonFieldType.NUMBER) .description("애니 고유 식별자"), @@ -301,10 +309,10 @@ void getShortReviewsWithCursor() throws Exception{ fieldWithPath("items[].content") .type(JsonFieldType.STRING) .description("짧은 리뷰 내용"), - fieldWithPath("items[].hasSpoiler") + fieldWithPath("items[].isSpoiler") .type(JsonFieldType.BOOLEAN) .description("스포일러 유무"), - fieldWithPath("items[].hasLike") + fieldWithPath("items[].isLike") .type(JsonFieldType.BOOLEAN) .description("짧은 리뷰 좋아요 유무"), fieldWithPath("items[].likeCount") @@ -330,27 +338,25 @@ void getShortReviewsWithCursor() throws Exception{ } - @Nested - @DisplayName("짧은 리뷰 수정") - class PatchShortReview{ - - @DisplayName("짧은 리뷰 수정 성공시 Http Status 200 반환") + @DisplayName("입덕포인트 조회") + class getAttractionPoint{ + @DisplayName("애니아이디와 회원이름으로 입덕포인트 조회") @Test @WithCustomMockMember(id = 1L, email = "john", password = "Qwer!234", role = Role.MEMBER) - void patchShortReview() throws Exception{ + void getAttractionPointsShortReview() throws Exception{ //given - Long reviewId = 1L; - PatchShortReviewReq req = ShortReviewTestUtils.createPatchShortReview(); - String content = gson.toJson(req); + String animeId = "1"; + String name = "회원 이름"; //when ResultActions actions = mockMvc.perform( - patch(BASE_URL +"/{reviewId}", reviewId) + get(BASE_URL + "/attraction-points") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}") - .content(content) + .param("animeId", animeId) + .param("name", name) ); //then @@ -361,7 +367,7 @@ void patchShortReview() throws Exception{ .andExpect(jsonPath("$.music").exists()) .andExpect(jsonPath("$.character").exists()) .andExpect(jsonPath("$.voiceActor").exists()) - .andDo(document("patchShortReview/success", + .andDo(document("shortReview/getAttractionPoints/success", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestHeaders( @@ -369,9 +375,16 @@ void patchShortReview() throws Exception{ .attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}")) .description("Header Cookie, 세션 쿠키") ), - pathParameters( - parameterWithName("reviewId") - .description("리뷰 식별자")), + queryParameters( + parameterWithName("animeId") + .attributes(field("constraints","애니메 ID, NotNull, Min(1)")) + .optional() + .description("애니 고유의 식별자"), + parameterWithName("name") + .attributes(field("constraints","회원 이름")) + .optional() + .description("회원 이름") + ), responseFields( fieldWithPath("drawing") .type(JsonFieldType.BOOLEAN) @@ -390,6 +403,64 @@ void patchShortReview() throws Exception{ .description("성우 입덕포인트")) )); } + } + + @Nested + @DisplayName("짧은 리뷰 수정") + class PatchShortReview{ + + + @DisplayName("짧은 리뷰 수정 성공시 Http Status 204 반환") + @Test + @WithCustomMockMember(id = 1L, email = "john", password = "Qwer!234", role = Role.MEMBER) + void patchShortReview() throws Exception{ + //given + Long reviewId = 1L; + ShortReviewReq req = ShortReviewTestUtils.createPatchShortReview(); + String content = gson.toJson(req); + + //when + ResultActions actions = mockMvc.perform( + patch(BASE_URL +"/{reviewId}", reviewId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.COOKIE, "oDuckio.sid={SESSION_VALUE}") + .content(content) + ); + + //then + actions + .andExpect(status().isNoContent()) + .andDo(document("patchShortReview/success", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(HttpHeaders.COOKIE) + .attributes(field("constraints", "oDuckio.sid={SESSION_VALUE}")) + .description("Header Cookie, 세션 쿠키") + ), + pathParameters( + parameterWithName("reviewId") + .description("리뷰 식별자")), + requestFields(attributes(key("title").value("Fields for shortReview creation")), + fieldWithPath("animeId") + .type(JsonFieldType.NUMBER) + .attributes(field("constraints","애니메 ID, NotNull, Min(1)")) + .description("리뷰를 등록할 애니 고유 식별 번호"), + fieldWithPath("name") + .type(JsonFieldType.STRING) + .attributes(field("constraints", "String만 가능합니다")) + .description("리뷰를 등록할 회원의 이름"), + fieldWithPath("hasSpoiler") + .type(JsonFieldType.BOOLEAN) + .attributes(field("constraints", "true 또는 false.")) + .description("스포일러 유무"), + fieldWithPath("content") + .type(JsonFieldType.STRING) + .attributes(field("constraints", "최소 10에서 100자 까지 입력 가능합니다.")) + .description("짧은 리뷰 내용")) + )); + } // @DisplayName("짧은 리뷰 수정 기존 내용과 동일할 시 400 BadRequest 응답") // @Test // void updateShortReviewFailsWhenSameContentAsBefore() throws Exception { diff --git a/src/test/java/io/oduck/api/global/utils/ShortReviewTestUtils.java b/src/test/java/io/oduck/api/global/utils/ShortReviewTestUtils.java index fa1058ee..4d66e163 100644 --- a/src/test/java/io/oduck/api/global/utils/ShortReviewTestUtils.java +++ b/src/test/java/io/oduck/api/global/utils/ShortReviewTestUtils.java @@ -2,22 +2,22 @@ import static io.oduck.api.global.utils.AnimeTestUtils.createAnime; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq; + +import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq; import io.oduck.api.domain.review.entity.ShortReview; import io.oduck.api.global.stub.MemberStub; public class ShortReviewTestUtils { - public static PostShortReviewReq createPostShoreReviewReq(){ - return new PostShortReviewReq( + public static ShortReviewReq createPostShoreReviewReq(){ + return new ShortReviewReq( getAnimeId(),getName(), isHasSpoiler(),getContent() ); } - public static PatchShortReviewReq createPatchShortReview(){ - return new PatchShortReviewReq( + public static ShortReviewReq createPatchShortReview(){ + return new ShortReviewReq( getAnimeId(), getName(), isHasSpoiler(),updateContent() ); } diff --git a/src/test/java/io/oduck/api/unit/shortReview/repository/ShortReviewRepositoryTest.java b/src/test/java/io/oduck/api/unit/shortReview/repository/ShortReviewRepositoryTest.java index 633e20b9..5dc922a3 100644 --- a/src/test/java/io/oduck/api/unit/shortReview/repository/ShortReviewRepositoryTest.java +++ b/src/test/java/io/oduck/api/unit/shortReview/repository/ShortReviewRepositoryTest.java @@ -210,7 +210,7 @@ void selectShortReviewsCountSuccess() { // then assertNotNull(shortReviewsCount); - assertEquals(2L, shortReviewsCount); + assertEquals(3L, shortReviewsCount); } } } \ No newline at end of file diff --git a/src/test/java/io/oduck/api/unit/shortReview/service/ShortReviewServiceTest.java b/src/test/java/io/oduck/api/unit/shortReview/service/ShortReviewServiceTest.java index d72c18d6..4e46ce29 100644 --- a/src/test/java/io/oduck/api/unit/shortReview/service/ShortReviewServiceTest.java +++ b/src/test/java/io/oduck/api/unit/shortReview/service/ShortReviewServiceTest.java @@ -2,12 +2,10 @@ import io.oduck.api.domain.anime.entity.Anime; import io.oduck.api.domain.anime.repository.AnimeRepository; -import io.oduck.api.domain.attractionPoint.dto.AttractionPointResDto; import io.oduck.api.domain.member.entity.Member; import io.oduck.api.domain.member.repository.MemberRepository; import io.oduck.api.domain.review.dto.ShortReviewDslDto.ShortReviewDsl; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq; -import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq; +import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq; import io.oduck.api.domain.review.dto.ShortReviewReqDto.Sort; import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewCountRes; import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewRes; @@ -35,7 +33,6 @@ import static io.oduck.api.global.utils.ShortReviewTestUtils.createPatchShortReview; import static io.oduck.api.global.utils.ShortReviewTestUtils.createPostShoreReviewReq; import static io.oduck.api.global.utils.ShortReviewTestUtils.createShortReview; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -71,7 +68,7 @@ void postShortReview(){ //given Long animeId = 1L; Long memberId = 1L; - PostShortReviewReq shortReviewReq = createPostShoreReviewReq(); + ShortReviewReq shortReviewReq = createPostShoreReviewReq(); ShortReview shortReview = createShortReview(); Anime anime = createAnime(); @@ -135,7 +132,7 @@ void patchShortReview(){ //given Long reviewId = 1L; Long memberId = 1L; - PatchShortReviewReq patchShortReviewReq = createPatchShortReview(); + ShortReviewReq patchShortReviewReq = createPatchShortReview(); given(shortReviewRepository.findById(reviewId)).willReturn(Optional.ofNullable(shortReview)); diff --git a/src/test/resources/db/data.sql b/src/test/resources/db/data.sql index e70e8e7a..80910697 100644 --- a/src/test/resources/db/data.sql +++ b/src/test/resources/db/data.sql @@ -74,7 +74,11 @@ INSERT INTO star_rating(score, anime_id, created_at, member_id, updated_at) VALU INSERT INTO star_rating(score, anime_id, created_at, member_id, updated_at) VALUES(3, 1, '2023-10-13 21:05:31.859', 3, '2023-10-13 21:05:31.859'); INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 1, '2023-10-10 21:05:31.859', 1, '2023-10-10 21:05:31.859', '최고'); -INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 2, '2023-10-11 21:05:31.859', 1, '2023-10-11 21:05:31.859', '힐링'); +INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 2, '2023-10-10 21:05:31.859', 1, '2023-10-10 21:05:31.859', '최고'); +INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 3, '2023-10-10 21:05:31.859', 1, '2023-10-10 21:05:31.859', '최고'); +INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 1, '2023-10-10 21:05:31.859', 2, '2023-10-10 21:05:31.859', '최고'); +INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 2, '2023-10-11 21:05:31.859', 2, '2023-10-11 21:05:31.859', '힐링'); +INSERT INTO short_review(has_spoiler, anime_id, created_at, member_id, updated_at, content) VALUES(0, 3, '2023-10-12 21:05:31.859', 2, '2023-10-13 21:05:31.859', '힐링'); INSERT INTO short_review_like(created_at, member_id, short_review_id, updated_at) VALUES('2023-10-10 21:05:31.859', 1, 1, '2023-10-10 21:05:31.859'); INSERT INTO short_review_like(created_at, member_id, short_review_id, updated_at) VALUES('2023-10-11 21:05:31.859', 1, 2, '2023-10-11 21:05:31.859'); \ No newline at end of file