001package usecase.comment;
002
003import model.entity.Comment;
004import model.entity.CommentVote;
005import model.entity.Post;
006import model.entity.User;
007import model.repository.CommentRepository;
008import model.repository.GenericRepository;
009import model.validation.CommentExists;
010import model.validation.PostExists;
011import usecase.auth.AuthenticationRequired;
012import usecase.auth.AuthorizationException;
013import usecase.auth.CurrentUser;
014import usecase.auth.DenyBannedUsers;
015
016import javax.enterprise.context.ApplicationScoped;
017import javax.inject.Inject;
018import javax.transaction.Transactional;
019import javax.validation.constraints.NotBlank;
020import javax.validation.constraints.Size;
021import java.time.Instant;
022import java.util.List;
023import java.util.Map;
024
025import static java.util.stream.Collectors.groupingBy;
026import static java.util.stream.Collectors.toList;
027
028/**
029 * Classe che fornisce i servizi relativi ai commenti.
030 */
031@ApplicationScoped
032@Transactional
033public class CommentService {
034    private GenericRepository genericRepository;
035    private CommentRepository commentRepo;
036    private CurrentUser currentUser;
037
038    private static final int MAX_COMMENT_DEPTH = 4;
039
040    protected CommentService(){}
041
042    @Inject
043    protected CommentService(GenericRepository genericRepository, CommentRepository commentRepository,
044                                            CurrentUser currentUser){
045        this.genericRepository = genericRepository;
046        this.commentRepo = commentRepository;
047        this.currentUser = currentUser;
048    }
049
050    /**
051     * Converte Comment in CommentDTO.
052     * @param comment commento da convertire
053     * @return commentDTO con i dati di comment
054     */
055    private CommentDTO map (Comment comment){
056        CommentVote commentVote = null;
057        if(currentUser.isLoggedIn()){
058            User user = genericRepository.findById(User.class, currentUser.getId());
059            commentVote = comment.getVote(user);
060        }
061
062        return CommentDTO.builder()
063                .id(comment.getId())
064                .authorUsername(comment.getAuthor().getUsername())
065                .authorId(comment.getAuthor().getId())
066                .creationDate(comment.getCreationDate())
067                .postId(comment.getPost().getId())
068                .vote(commentVote == null ? 0 : commentVote.getVote())
069                .content(comment.getContent())
070                .votes(comment.getVotesCount() == null ? 0 : comment.getVotesCount())
071                .creationDate(comment.getCreationDate()  == null ? Instant.now() : comment.getCreationDate())
072                .parentCommentId(comment.getParentComment() == null ? 0 : comment.getParentComment().getId())
073                .build();
074    }
075
076    /**
077     * Ritorna una mappa la cui chiave è l'id del commento padre e il valore una lista di CommentDTO
078     * @param postId l'id di un post esistente di cui si vuole ottenere i commenti
079     * @return mappa con i commenti del post
080     */
081    public Map<Integer,List<CommentDTO>> getPostComments(@PostExists int postId){
082        List<Comment> comments = commentRepo.getByPost(genericRepository.findById(Post.class, postId), MAX_COMMENT_DEPTH);
083        return comments.stream().map(this::map).collect(groupingBy(CommentDTO::getParentCommentId, toList()));
084    }
085
086    /**
087     * Ritorna una mappa la cui chiave è l'id del commento padre e il valore una lista di CommentDTO
088     * @param commentId l'id di un commento esistente di cui si vuole ottenere le risposte
089     * @return mappa con le risposte al commento
090     */
091    public Map<Integer, List<CommentDTO>> getReplies(@CommentExists int commentId){
092        List<Comment> comments = commentRepo.getReplies(genericRepository.findById(Comment.class, commentId), MAX_COMMENT_DEPTH);
093        return comments.stream().map(this::map).collect(groupingBy(CommentDTO::getParentCommentId, toList()));
094    }
095
096    /**
097     * Ritorna un commento dato il suo id
098     * @param id l'id di un commento esistente
099     * @return commento avente l'id specificato
100     */
101    public CommentDTO getComment(@CommentExists int id){
102        return map(genericRepository.findById(Comment.class, id));
103    }
104
105
106    /**
107     * Cancella un commento dato il suo id
108     * @param id l'id di un commento esistente
109     */
110    @AuthenticationRequired
111    public void delete(@CommentExists int id){
112        Comment comment = genericRepository.findById(Comment.class, id);
113        if(currentUser.getId() != comment.getAuthor().getId() && !currentUser.isAdmin())
114            throw new AuthorizationException();
115        genericRepository.remove(genericRepository.findById(Comment.class, id));
116    }
117
118    /**
119     * Modifica un commento dato il suo id
120     * @param id l'id di un commento esistente
121     * @param text stringa con testo da sostituire
122     */
123    @AuthenticationRequired
124    public void editComment(@CommentExists int id, @NotBlank @Size(max=65535) String text){
125        Comment comment = genericRepository.findById(Comment.class, id);
126        if(currentUser.getId() != comment.getAuthor().getId() && !currentUser.isAdmin())
127            throw new AuthorizationException();
128        comment.setContent(text);
129    }
130
131    /**
132     * Crea un nuovo commento e ne restituisce l'id
133     * @param text una stringa non vuota di massimo 65535 caratteri
134     * @param postId id di un post esistente
135     * @return id del commento creato
136     */
137    @AuthenticationRequired
138    @DenyBannedUsers
139    public int newComment(@NotBlank @Size(max=65535) String text,
140                          @PostExists int postId){
141        User user = genericRepository.findById(User.class, currentUser.getId());
142
143        Comment comment = new Comment();
144        comment.setAuthor(user);
145        comment.setContent(text);
146        comment.setPost(genericRepository.findById(Post.class, postId));
147        return genericRepository.insert(comment).getId();
148    }
149
150    /**
151     * Crea una risposta a un commento e ne restituisce l'id
152     * @param text una stringa non vuota di massimo 1000 caratteri
153     * @param parentCommentId id di un commento esistente
154     * @return id del commento creato
155     */
156    @AuthenticationRequired
157    public int newCommentReply(@NotBlank @Size(max=65535) String text,
158                               @CommentExists int parentCommentId){
159        User user = genericRepository.findById(User.class, currentUser.getId());
160        Comment parent = genericRepository.findById(Comment.class, parentCommentId);
161
162        Comment comment = new Comment();
163        comment.setAuthor(user);
164        comment.setContent(text);
165        comment.setPost(parent.getPost());
166        comment.setParentComment(parent);
167        return genericRepository.insert(comment).getId();
168    }
169
170}