Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] issue120: 스터디 리뷰 작성 #140

Merged
merged 26 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ef0e0ff
fix: 생성일자에 null이 들어와서 검증 시 발생하는 예외 처리
sc0116 Jul 26, 2022
38dad80
test: 필수 데이터 없는 경우 401 반환하는 인수 테스트 추가
sc0116 Jul 26, 2022
7ea0057
feat: 필수 데이터 없는 경우 401 반환하는 기능 구현
sc0116 Jul 26, 2022
c7fa3cc
feat: 스터디 시작 전에 후기 작성 시 예외 반환 기능 구현
sc0116 Jul 26, 2022
1a42d6b
feat: 스터디 참여 여부 기능 구현
sc0116 Jul 26, 2022
51fb4d8
feat: 리뷰 작성 기능 구현
sc0116 Jul 26, 2022
b07fc33
refactor: BaseTime 제거
sc0116 Jul 26, 2022
1230c32
test: 리뷰 관련 인수테스트 실패
sc0116 Jul 27, 2022
56f8bc3
test: 리뷰 관련 인수테스트 작성
sc0116 Jul 27, 2022
54644f4
test: 리뷰 인수테스트 및 HTTP 요청 테스트 추가
sc0116 Jul 27, 2022
4d0b365
refactor: 예외 메세지 및 메소드명 수정
sc0116 Jul 27, 2022
7cdfb2f
refactor: 사용하지 않는 클래스 제거 및 메소드명 수정
sc0116 Jul 27, 2022
eea26ed
refactor: 스터디 시작 전에 후기 작성 시 예외 반환 기능 로직 변경
sc0116 Jul 27, 2022
81b19ff
style: EOF 처리
sc0116 Jul 27, 2022
94dcdf4
style: 상수명 대문자로 변경
sc0116 Jul 28, 2022
68cac97
test: DisplayName 및 메소드명 수정
sc0116 Jul 28, 2022
b07373d
refactor: 정적 팩터리 메소드 대신 new 생성자로 변경
sc0116 Jul 28, 2022
196b3dc
refactor: 리뷰를 작성할 스터디에 참여했는지 판단하는 로직 수정
sc0116 Jul 28, 2022
dca3c70
refactor: 리뷰 작성 로직에서 검증과 생성 순서 변경
sc0116 Jul 28, 2022
643bfb0
test: 리뷰 관련 테스트 수정
sc0116 Jul 28, 2022
2bb5f55
refactor: 리뷰 관련 Controller와 Service에서 명령을 위한 것과 사용자 조회를 위한 것으로 분리
sc0116 Jul 28, 2022
36d8a34
style: EOF 처리
sc0116 Jul 29, 2022
11fe2f1
refactor: 리뷰 작성 시 해당 스터디 참여 여부 로직에서 스터디장 판단 로직 추가
sc0116 Jul 29, 2022
6a61f2b
chore: develop merge 충돌 해결
sc0116 Jul 29, 2022
f41383e
refactor: 사용하지 않는 부분 제거
sc0116 Jul 29, 2022
9bab968
chore: develop merge 충돌 해결
sc0116 Jul 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ public class AuthenticationInterceptor implements HandlerInterceptor {
private final TokenProvider tokenProvider;

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler) {
if (isPreflight(request)) {
return true;
}

if (request.getMethod().equals("POST") && request.getServletPath().equals("/api/studies") ||
request.getMethod().equals("GET") && request.getServletPath().equals("/api/my/studies")) {
String token = AuthenticationExtractor.extract(request);
if (request.getMethod().equals("POST") && validatePostPath(request) ||
request.getMethod().equals("GET") && validateGetPath(request)) {
final String token = AuthenticationExtractor.extract(request);
validateToken(token);

request.setAttribute("payload", tokenProvider.getPayload(token));
Expand All @@ -42,4 +43,13 @@ private void validateToken(String token) {
throw new UnauthorizedException("유효하지 않은 토큰입니다.");
}
}

private boolean validatePostPath(final HttpServletRequest request) {
return request.getServletPath().equals("/api/studies") ||
request.getServletPath().matches("/api/studies/\\d+/reviews");
}

private boolean validateGetPath(final HttpServletRequest request) {
return request.getServletPath().equals("/api/my/studies");
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.woowacourse.moamoa.common.advice;

import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;

import com.woowacourse.moamoa.common.advice.response.ErrorResponse;
import com.woowacourse.moamoa.common.exception.BadRequestException;
import com.woowacourse.moamoa.common.exception.InvalidFormatException;
import com.woowacourse.moamoa.common.exception.NotFoundException;
import com.woowacourse.moamoa.common.exception.UnauthorizedException;
import com.woowacourse.moamoa.study.domain.exception.InvalidPeriodException;
import com.woowacourse.moamoa.study.service.exception.FailureParticipationException;
Expand All @@ -18,26 +19,32 @@
@RestControllerAdvice
public class CommonControllerAdvice {

@ExceptionHandler({
MethodArgumentTypeMismatchException.class,
HttpMessageNotReadableException.class
})
public ResponseEntity<ErrorResponse> handleBadRequest() {
return ResponseEntity.badRequest().body(new ErrorResponse("잘못된 요청 형식입니다."));
}

@ExceptionHandler({
InvalidFormatException.class,
InvalidPeriodException.class,
BadRequestException.class,
FailureParticipationException.class
})
public ResponseEntity<ErrorResponse> handleBadRequest(final Exception e) {
return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
}

@ExceptionHandler({
MethodArgumentTypeMismatchException.class,
HttpMessageNotReadableException.class
})
public ResponseEntity<ErrorResponse> handleBadRequest() {
return ResponseEntity.badRequest().body(new ErrorResponse("잘못된 요청 형식입니다."));
@ExceptionHandler({UnauthorizedException.class})
public ResponseEntity<Void> handleUnauthorized(final Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

@ExceptionHandler
public ResponseEntity<Void> handleUnauthorized(final UnauthorizedException e) {
return ResponseEntity.status(UNAUTHORIZED).build();
@ExceptionHandler({NotFoundException.class})
public ResponseEntity<ErrorResponse> handleNotFound(final Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse(e.getMessage()));
}

@ExceptionHandler(RuntimeException.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.woowacourse.moamoa.common.exception;

public class BadRequestException extends RuntimeException {

public BadRequestException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.woowacourse.moamoa.common.exception;

public class NotFoundException extends RuntimeException {

public NotFoundException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public MemberDao(final NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public List<MemberData> findMembersByStudyId(Long studyId) {
String sql = "SELECT github_id, username, image_url, profile_url "
public List<MemberData> findMembersByStudyId(final Long studyId) {
final String sql = "SELECT github_id, username, image_url, profile_url "
Comment on lines -29 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿굿!!👍

+ "FROM member JOIN study_member ON member.id = study_member.member_id "
+ "WHERE study_member.study_id = :id";
return jdbcTemplate.query(sql, Map.of("id", studyId), ROW_MAPPER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.woowacourse.moamoa.member.domain.Member;
import com.woowacourse.moamoa.member.domain.repository.MemberRepository;
import com.woowacourse.moamoa.member.query.data.MemberData;
import com.woowacourse.moamoa.member.service.exception.InvalidMemberException;
import com.woowacourse.moamoa.member.service.exception.MemberNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -24,7 +24,7 @@ public void saveOrUpdate(final Member member) {

public MemberData searchBy(final Long githubId) {
final Member member = memberRepository.findByGithubId(githubId)
.orElseThrow(() -> new InvalidMemberException("사용자를 찾을 수 없습니다."));
.orElseThrow(MemberNotFoundException::new);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명확해서 좋아요 👍

return new MemberData(member.getGithubId(), member.getUsername(), member.getImageUrl(), member.getProfileUrl());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woowacourse.moamoa.member.service.exception;

import com.woowacourse.moamoa.common.exception.NotFoundException;

public class MemberNotFoundException extends NotFoundException {

public MemberNotFoundException() {
super("회원을 찾을 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woowacourse.moamoa.member.service.exception;

import com.woowacourse.moamoa.common.exception.UnauthorizedException;

public class NotParticipatedMemberException extends UnauthorizedException {

public NotParticipatedMemberException() {
super("스터디에 참여한 회원만 후기를 작성할 수 있습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
package com.woowacourse.moamoa.review.controller;

import com.woowacourse.moamoa.auth.config.AuthenticationPrincipal;
import com.woowacourse.moamoa.review.service.ReviewService;
import com.woowacourse.moamoa.review.service.request.SizeRequest;
import com.woowacourse.moamoa.review.service.response.ReviewsResponse;
import com.woowacourse.moamoa.review.service.request.WriteReviewRequest;
import java.net.URI;
import javax.validation.Valid;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/studies")
@RequestMapping("/api/studies/{study-id}/reviews")
@RequiredArgsConstructor
public class ReviewController {

private final ReviewService reviewService;

@GetMapping("/{study-id}/reviews")
public ResponseEntity<ReviewsResponse> getReviews(
@PathVariable(name = "study-id") Long studyId,
@RequestParam(name = "size", required = false, defaultValue = "") SizeRequest sizeRequest
@PostMapping
public ResponseEntity<Void> writeReview(
@AuthenticationPrincipal final Long githubId,
@PathVariable(name = "study-id") final Long studyId,
@Valid @RequestBody final WriteReviewRequest writeReviewRequest
) {
final ReviewsResponse reviewsResponse = reviewService.getReviewsByStudy(studyId, sizeRequest);
return ResponseEntity.ok(reviewsResponse);
final Long id = reviewService.writeReview(githubId, studyId, writeReviewRequest);
return ResponseEntity.created(URI.create("/api/studies/" + studyId + "/reviews/" + id)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.woowacourse.moamoa.review.controller;

import com.woowacourse.moamoa.review.service.SearchingReviewService;
import com.woowacourse.moamoa.review.service.request.SizeRequest;
import com.woowacourse.moamoa.review.service.response.ReviewsResponse;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/studies/{study-id}/reviews")
@RequiredArgsConstructor
public class SearchingReviewController {

private final SearchingReviewService searchingReviewService;

@GetMapping
public ResponseEntity<ReviewsResponse> getReviews(
@PathVariable(name = "study-id") Long studyId,
@RequestParam(name = "size", required = false, defaultValue = "") SizeRequest sizeRequest
) {
final ReviewsResponse reviewsResponse = searchingReviewService.getReviewsByStudy(studyId, sizeRequest);
return ResponseEntity.ok(reviewsResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import lombok.NoArgsConstructor;

@Embeddable
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
@Getter
@NoArgsConstructor(access = PROTECTED)
public class AssociatedStudy {

@Column(name = "study_id", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
import org.springframework.data.annotation.LastModifiedDate;

@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
@Getter
public class Review {

@Id
Expand All @@ -39,10 +39,17 @@ public class Review {
private String content;

@CreatedDate
@Column(nullable = false)
@Column(nullable = false, updatable = false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updatable false 굿!!

private LocalDateTime createdDate;

@LastModifiedDate
@Column(nullable = false)
private LocalDateTime lastModifiedDate;

public Review(
final AssociatedStudy associatedStudy, final Member member, final String content,
final LocalDateTime createdDate, final LocalDateTime lastModifiedDate
) {
this(null, associatedStudy, member, content, createdDate, lastModifiedDate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woowacourse.moamoa.review.domain.exception;

import com.woowacourse.moamoa.common.exception.BadRequestException;

public class WritingReviewBadRequestException extends BadRequestException {

public WritingReviewBadRequestException() {
super("스터디 시작 전 후기를 작성할 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.woowacourse.moamoa.review.domain.repository;

import com.woowacourse.moamoa.review.domain.Review;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaReviewRepository extends JpaRepository<Review, Long>, ReviewRepository {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.woowacourse.moamoa.review.domain.repository;

import com.woowacourse.moamoa.review.domain.AssociatedStudy;
import com.woowacourse.moamoa.review.domain.Review;
import java.util.List;

public interface ReviewRepository {

Review save(Review review);

List<Review> findAllByAssociatedStudy(AssociatedStudy associatedStudy);
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
package com.woowacourse.moamoa.review.service;

import com.woowacourse.moamoa.review.query.ReviewDao;
import com.woowacourse.moamoa.review.query.data.ReviewData;
import com.woowacourse.moamoa.review.service.request.SizeRequest;
import com.woowacourse.moamoa.review.service.response.ReviewsResponse;
import java.util.List;
import com.woowacourse.moamoa.member.domain.Member;
import com.woowacourse.moamoa.member.domain.repository.MemberRepository;
import com.woowacourse.moamoa.member.service.exception.MemberNotFoundException;
import com.woowacourse.moamoa.member.service.exception.NotParticipatedMemberException;
import com.woowacourse.moamoa.review.domain.AssociatedStudy;
import com.woowacourse.moamoa.review.domain.Review;
import com.woowacourse.moamoa.review.domain.exception.WritingReviewBadRequestException;
import com.woowacourse.moamoa.review.domain.repository.ReviewRepository;
import com.woowacourse.moamoa.review.service.request.WriteReviewRequest;
import com.woowacourse.moamoa.study.domain.Participant;
import com.woowacourse.moamoa.study.domain.Study;
import com.woowacourse.moamoa.study.domain.repository.StudyRepository;
import com.woowacourse.moamoa.study.service.exception.StudyNotFoundException;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@Transactional
@RequiredArgsConstructor
public class ReviewService {

private final ReviewDao reviewDao;
private final ReviewRepository reviewRepository;
private final MemberRepository memberRepository;
private final StudyRepository studyRepository;

public ReviewsResponse getReviewsByStudy(Long studyId, SizeRequest sizeRequest) {
final List<ReviewData> allReviews = reviewDao.findAllByStudyId(studyId);
public Long writeReview(final Long githubId, final Long studyId, final WriteReviewRequest writeReviewRequest) {
final Study study = studyRepository.findById(studyId)
.orElseThrow(StudyNotFoundException::new);
final Member member = memberRepository.findByGithubId(githubId)
.orElseThrow(MemberNotFoundException::new);

if (sizeRequest.isEmpty() || sizeRequest.isMoreThan(allReviews.size())) {
return new ReviewsResponse(allReviews);
final Participant participant = new Participant(member.getId());
if (!study.isParticipated(participant)) {
throw new NotParticipatedMemberException();
}

return new ReviewsResponse(allReviews.subList(0, sizeRequest.getValue()), allReviews.size());
}
final LocalDateTime reviewCreatedDate = LocalDateTime.now();
if (study.isBeforeThanStudyStartDate(reviewCreatedDate.toLocalDate())) {
throw new WritingReviewBadRequestException();
}

final AssociatedStudy associatedStudy = new AssociatedStudy(studyId);
final Review review = new Review(
associatedStudy, member, writeReviewRequest.getContent(), reviewCreatedDate, reviewCreatedDate
);

return reviewRepository.save(review).getId();
}
}
Loading