package com.example.ec.web; import com.example.ec.domain.Tour; import com.example.ec.domain.TourRating; import com.example.ec.domain.TourRatingPk; import com.example.ec.repo.TourRatingRepository; import com.example.ec.repo.TourRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Collectors; @RestController @RequestMapping(path = "/tours/{tourId}/ratings") public class TourRatingController { TourRatingRepository tourRatingRepository; TourRepository tourRepository; @Autowired public TourRatingController(TourRatingRepository tourRatingRepository, TourRepository tourRepository) { this.tourRatingRepository = tourRatingRepository; this.tourRepository = tourRepository; } protected TourRatingController() {} @PostMapping @ResponseStatus(HttpStatus.CREATED) public void createTourRating(@PathVariable(value = "tourId") int tourId, @RequestBody @Validated RatingDto ratingDto) { Tour tour = verifyTour(tourId); tourRatingRepository.save(new TourRating( new TourRatingPk(tour, ratingDto.getCustomerId()), ratingDto.getScore(), ratingDto.getComment())); } @GetMapping public List<RatingDto> getAllRatingsForTour(@PathVariable(value = "tourId") int tourId) { verifyTour(tourId); return tourRatingRepository.findByPkTourId(tourId) // stream it .stream() // map to dto .map(RatingDto::new) // convert to a list .collect(Collectors.toList()); } @GetMapping(path = "/average") public Map<String, Double> getAverage(@PathVariable(value = "tourId") int tourId) { verifyTour(tourId); return Map.of("average", tourRatingRepository.findByPkTourId(tourId).stream() .mapToInt(TourRating::getScore).average() .orElseThrow(() -> new NoSuchElementException("Tour has no Ratings"))); } /** * Update score and comment of a Tour Rating * * @param tourId tour identifier * @param ratingDto rating Data Transfer Object * @return The modified Rating DTO. */ @PutMapping public RatingDto updateWithPut(@PathVariable(value = "tourId") int tourId, @RequestBody @Validated RatingDto ratingDto) { TourRating rating = verifyTourRating(tourId, ratingDto.getCustomerId()); rating.setScore(ratingDto.getScore()); rating.setComment(ratingDto.getComment()); return new RatingDto(tourRatingRepository.save(rating)); } /** * Update score or comment of a Tour Rating * * @param tourId tour identifier * @param ratingDto rating Data Transfer Object * @return The modified Rating DTO. */ @PatchMapping public RatingDto updateWithPatch(@PathVariable(value = "tourId") int tourId, @RequestBody @Validated RatingDto ratingDto) { TourRating rating = verifyTourRating(tourId, ratingDto.getCustomerId()); if (ratingDto.getScore() != null) { rating.setScore(ratingDto.getScore()); } if (ratingDto.getComment() != null) { rating.setComment(ratingDto.getComment()); } return new RatingDto(tourRatingRepository.save(rating)); } /** * Delete a Rating of a tour made by a customer * * @param tourId tour identifier * @param customerId customer identifier */ @DeleteMapping(path = "/{customerId}") public void delete(@PathVariable(value = "tourId") int tourId, @PathVariable(value = "customerId") int customerId) { TourRating rating = verifyTourRating(tourId, customerId); tourRatingRepository.delete(rating); } /** * Verify and return the TourRating for a particular tourId and Customer * @param tourId tour identifier * @param customerId customer identifier * @return the found TourRating * @throws NoSuchElementException if no TourRating found */ private TourRating verifyTourRating(int tourId, int customerId) throws NoSuchElementException { return tourRatingRepository.findByPkTourIdAndPkCustomerId(tourId, customerId).orElseThrow(() -> new NoSuchElementException("Tour-Rating pair for request(" + tourId + " for customer" + customerId)); } /** * Verify and return the Tour given a tourId. * * @param tourId tour identifier * @return the found Tour * @throws NoSuchElementException if no Tour found. */ private Tour verifyTour(int tourId) throws NoSuchElementException { return tourRepository.findById(tourId).orElseThrow(() -> new NoSuchElementException("Tour does not exist " + tourId)); } /** * Exception handler if NoSuchElementException is thrown in this Controller * * @param ex exception * @return Error message String. */ @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(NoSuchElementException.class) public String return400(NoSuchElementException ex) { return ex.getMessage(); } }
DTO:
package com.example.ec.web; import com.example.ec.domain.TourRating; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * Data Transfer Object for Rating a Tour * */ public class RatingDto { @Min(0) @Max(5) private Integer score; @Size(max = 255) private String comment; @NotNull private Integer customerId; /** * Construct a RatingDto from a fully instantiated TourRating. * * @param tourRating Tour Rating Object */ public RatingDto(TourRating tourRating) { this(tourRating.getScore(), tourRating.getComment(), tourRating.getPk().getCustomerId()); } /** * Constructor to fully initialize the RatingDto * * @param score score 1-5 * @param comment comment * @param customerId customer identifier */ private RatingDto(Integer score, String comment, Integer customerId) { this.score = score; this.comment = comment; this.customerId = customerId; } protected RatingDto() {} public Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Integer getCustomerId() { return customerId; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } }