Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,25 @@
package ru.practicum.comment.controller;

import ru.practicum.comment.service.CommentService;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("admin/comments")
public class CommentAdminController {
private final CommentService commentService;

@DeleteMapping("/{commentId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteComment(@PathVariable long commentId) {
log.info("Admin delete comment requested for commentId={}", commentId);

commentService.deleteComment(commentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package ru.practicum.comment.controller;

import java.util.Collection;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;

import ru.practicum.comment.dto.CommentDto;
import ru.practicum.comment.dto.NewCommentDto;
import ru.practicum.comment.dto.UpdateCommentDto;
import ru.practicum.comment.service.*;

import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/users/{userId}/comments")
public class CommentPrivateController {
private final CommentService commentService;

@GetMapping
public Collection<CommentDto> getAllCommentsPaged(
@PathVariable Long userId,
@RequestParam(defaultValue = "0") @PositiveOrZero int from,
@RequestParam(defaultValue = "10") @Positive int size) {
log.info("Private get all comments requested by userId:{}", userId);
CommentsPrivateGetRequest request = new CommentsPrivateGetRequest(userId, from, size);
return commentService.getAllCommentsOfUserPaged(request);
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public CommentDto createComment(
@PathVariable long userId, @Valid @RequestBody NewCommentDto newComment) {
CommentsCreateRequest request = new CommentsCreateRequest(userId, newComment);
log.info("Create new comment requested");
return commentService.createComment(request);
}

@PatchMapping("/{commentId}")
public CommentDto updateComment(
@PathVariable long userId,
@PathVariable long commentId,
@Valid @RequestBody UpdateCommentDto updateComment) {

CommentsUpdateRequest request = new CommentsUpdateRequest(userId, commentId, updateComment);

log.info("Update comment requested by userId:{}, commentId:{}", userId, commentId);

return commentService.updateComment(request);
}

@DeleteMapping("/{commentId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteComment(@PathVariable long userId, @PathVariable long commentId) {

log.info("Delete comment requested by userId:{}, commentId:{}", userId, commentId);

commentService.deleteCommentByUser(userId, commentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ru.practicum.comment.controller;

import ru.practicum.comment.dto.CommentDto;
import ru.practicum.comment.service.CommentService;

import org.springframework.web.bind.annotation.*;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/comments")
public class CommentPublicController {
private final CommentService commentService;

@GetMapping("/{commentId}")
public CommentDto getComments(@PathVariable Long commentId) {

log.info("Get comment by id={} requested", commentId);
return commentService.getById(commentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ru.practicum.comment.dto;

import java.time.LocalDateTime;

import ru.practicum.user.dto.UserShortDto;

public record CommentDto(
Long id, String text, UserShortDto author, LocalDateTime created, boolean edited) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.practicum.comment.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record NewCommentDto(@NotNull Long eventId, @NotBlank @Size(max = 500) String text) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package ru.practicum.comment.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record UpdateCommentDto(@NotBlank @Size(max = 500) String text) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ru.practicum.comment.mapper;

import java.time.LocalDateTime;

import ru.practicum.comment.dto.CommentDto;
import ru.practicum.comment.dto.NewCommentDto;
import ru.practicum.comment.model.Comment;
import ru.practicum.event.model.Event;
import ru.practicum.user.mapper.UserMapper;
import ru.practicum.user.model.User;

import lombok.experimental.UtilityClass;

@UtilityClass
public class CommentMapper {

public CommentDto toCommentDto(Comment comment, User author) {
return new CommentDto(
comment.getId(),
comment.getText(),
UserMapper.mapToUserShortDto(author),
comment.getCreated(),
comment.isEdited());
}

public Comment toEntity(NewCommentDto newCommentDto, User author, Event event) {
return new Comment(null, newCommentDto.text(), author, event, LocalDateTime.now(), false);
}
}
43 changes: 43 additions & 0 deletions main-service/src/main/java/ru/practicum/comment/model/Comment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.practicum.comment.model;

import java.time.LocalDateTime;

import jakarta.persistence.*;

import ru.practicum.event.model.Event;
import ru.practicum.user.model.User;

import lombok.experimental.FieldDefaults;
import lombok.*;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "comment")
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Comment {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;

@Column(nullable = false)
String text;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id", nullable = false)
User author;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "event_id", nullable = false)
Event event;

@Column(nullable = false)
LocalDateTime created;

@Column(nullable = false)
boolean edited;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ru.practicum.comment.repository;

import java.util.List;

import ru.practicum.comment.model.Comment;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface CommentRepository extends JpaRepository<Comment, Long> {

@EntityGraph(attributePaths = "author")
Page<Comment> findAllByEventId(Long eventId, Pageable pageable);

List<Comment> findAllByAuthorId(Long authorId, Pageable pageable);

@Query(
"""
select new ru.practicum.comment.repository.EventCommentCount(c.event.id, count(c))
from Comment c
where c.event.id in :eventIds
group by c.event.id
""")
List<EventCommentCount> countCommentsByEventIds(@Param("eventIds") List<Long> eventIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package ru.practicum.comment.repository;

public record EventCommentCount(Long eventId, Long count) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ru.practicum.comment.service;

import java.util.Collection;

import ru.practicum.comment.dto.CommentDto;

public interface CommentService {
Collection<CommentDto> getAllCommentsPaged(CommentsPublicGetRequest request);

Collection<CommentDto> getAllCommentsOfUserPaged(CommentsPrivateGetRequest request);

void deleteComment(long commentId);

void deleteCommentByUser(long userId, long commentId);

CommentDto createComment(CommentsCreateRequest request);

CommentDto updateComment(CommentsUpdateRequest request);

CommentDto getById(Long commentId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package ru.practicum.comment.service;

import java.util.Collection;
import java.util.List;
import java.util.Optional;

import ru.practicum.comment.dto.CommentDto;
import ru.practicum.comment.mapper.CommentMapper;
import ru.practicum.comment.model.Comment;
import ru.practicum.comment.repository.CommentRepository;
import ru.practicum.event.model.Event;
import ru.practicum.event.repository.EventRepository;
import ru.practicum.exception.ForbiddenAccessException;
import ru.practicum.exception.NotFoundException;
import ru.practicum.user.model.User;
import ru.practicum.user.repository.UserRepository;

import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {
private final CommentRepository commentRepository;
private final UserRepository userRepository;
private final EventRepository eventRepository;

@Override
@Transactional(readOnly = true)
public Collection<CommentDto> getAllCommentsPaged(CommentsPublicGetRequest request) {
if (!eventRepository.existsById(request.eventId())) {
throw new NotFoundException("Event with id=%d not found".formatted(request.eventId()));
}

Page<Comment> comments =
commentRepository.findAllByEventId(request.eventId(), request.getPageable());

return comments.stream()
.map(comment -> CommentMapper.toCommentDto(comment, comment.getAuthor()))
.toList();
}

@Override
@Transactional(readOnly = true)
public Collection<CommentDto> getAllCommentsOfUserPaged(CommentsPrivateGetRequest request) {
User user = getUserByIdOrThrow(request.userId());

List<Comment> comments =
commentRepository.findAllByAuthorId(request.userId(), request.getPageable());

return comments.stream().map(comment -> CommentMapper.toCommentDto(comment, user)).toList();
}

@Override
public void deleteComment(long commentId) {
commentRepository.deleteById(commentId);
}

@Override
public void deleteCommentByUser(long userId, long commentId) {
User user = getUserByIdOrThrow(userId);
Comment comment = getCommentByIdOrThrow(commentId);
if (!comment.getAuthor().getId().equals(user.getId())) {
throw new ForbiddenAccessException("You are not allowed to delete others comments");
}
commentRepository.deleteById(commentId);
}

@Override
public CommentDto createComment(CommentsCreateRequest request) {
Event event = getEventByIdOrThrow(request.newComment().eventId());
User user = getUserByIdOrThrow(request.userId());

Comment newComment = CommentMapper.toEntity(request.newComment(), user, event);
Comment saved = commentRepository.save(newComment);
return CommentMapper.toCommentDto(saved, user);
}

@Override
public CommentDto updateComment(CommentsUpdateRequest request) {
User user = getUserByIdOrThrow(request.userId());
Comment comment = getCommentByIdOrThrow(request.commentId());
if (!comment.getAuthor().getId().equals(user.getId())) {
throw new ForbiddenAccessException("You are not allowed to update others comments");
}

comment.setText(request.updateComment().text());
comment.setEdited(true);
Comment saved = commentRepository.save(comment);
return CommentMapper.toCommentDto(saved, user);
}

@Override
public CommentDto getById(Long commentId) {
Comment comment = getCommentByIdOrThrow(commentId);
return CommentMapper.toCommentDto(comment, comment.getAuthor());
}

private Event getEventByIdOrThrow(Long eventId) {
Optional<Event> eventOptional = eventRepository.findById(eventId);
return eventOptional.orElseThrow(
NotFoundException.supplier("Event with id=%d not found", eventId));
}

private User getUserByIdOrThrow(Long userId) {
Optional<User> optionalUser = userRepository.findById(userId);
return optionalUser.orElseThrow(
NotFoundException.supplier("User with id=%d not found", userId));
}

private Comment getCommentByIdOrThrow(Long commentId) {
Optional<Comment> optionalComment = commentRepository.findById(commentId);
return optionalComment.orElseThrow(
NotFoundException.supplier("Comment with id=%d not found", commentId));
}
}
Loading