PostService.java

package usecase.post;

import media.MediaRepository;
import media.ReadLimitExceededException;
import media.validation.Image;
import model.entity.Post;
import model.entity.Section;
import model.entity.User;
import model.repository.GenericRepository;
import model.repository.PostRepository;
import model.validation.PostExists;
import model.validation.SectionExists;
import usecase.auth.AuthenticationRequired;
import usecase.auth.AuthorizationException;
import usecase.auth.CurrentUser;
import usecase.auth.DenyBannedUsers;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;

import static model.entity.Post.Type.IMG;
import static model.entity.Post.Type.TEXT;
import static usecase.post.PostSearchForm.SortCriteria.NEWEST;

/**
 * Classe che fornisce i servizi relativi ai post.
 */
@ApplicationScoped
@Transactional
public class PostService {
    private GenericRepository genericRepository;
    private PostRepository postRepo;
    private MediaRepository bcRepo;
    private CurrentUser currentUser;

    protected PostService(){}

    @Inject
    protected PostService(GenericRepository genericRepository, PostRepository postRepo,
                          MediaRepository bcRepo, CurrentUser currentUser){
        this.genericRepository = genericRepository;
        this.postRepo = postRepo;
        this.bcRepo = bcRepo;
        this.currentUser = currentUser;
    }

    /**
     * Converte post in PostPage.
     * @param post post da convertire
     * @return PostPage con i dati di post
     */
    private PostPage mapPost(Post post){
        User user = null;
        if(currentUser.isLoggedIn())
            user = genericRepository.findById(User.class,currentUser.getId());

        //converte persistence.Post.Type in usecase.post.PostType (ew)
        PostType postType = post.getType() == IMG ? PostType.IMG : PostType.TEXT;

        return PostPage.builder()
                .id(post.getId())
                .title(post.getTitle())
                .vote(post.getVote(user) == null ? 0 : post.getVote(user).getVote())
                .votes(post.getVotesCount() == null ? 0 : post.getVotesCount())
                .sectionName(post.getSection().getName())
                .authorName(post.getAuthor().getUsername())
                .sectionId(post.getSection().getId())
                .authorId(post.getAuthor().getId())
                .content(post.getContent())
                .creationDate(post.getCreationDate() == null ? Instant.now() : post.getCreationDate())
                .nComments(post.getCommentCount())
                .type(postType)
                .build();
    }

    /**
     * Ritorna un entità DTO relativa ad un post
     * @param id identificativo post
     * @return DTO di un post
     */
    public PostPage getPost(@PostExists int id){
        Post p = genericRepository.findById(Post.class,id);
        return mapPost(p);
    }

    /**
     * Ritorna una lista di post che rispettano determinati parametri
     * @param form entità con i parametri di ricerca
     * @return lista di PostPage
     */
    @Transactional
    public List<PostPage> loadPosts(PostSearchForm form){
        final int elementsPerPage = 10;
        PostRepository.PostFinder finder = postRepo.getFinder();

        finder.limit(elementsPerPage);

        int page = form.getPage() > 0 ? form.getPage() : 1;
        finder.offset((page == 1) ? 0 : (page-1) * elementsPerPage+1);

        if(form.getContent() != null) {
            finder.byContent(form.getContent());
        }
        if(form.isIncludeBody()){
            finder.includeBody();
        }
        if(form.getSectionName() != null){
            Section section = genericRepository.findByNaturalId(Section.class, form.getSectionName());
            if(section != null){
                finder.bySection(section);
            }
        }
        if(form.getAuthorName() != null){
            User user = genericRepository.findByNaturalId(User.class, form.getAuthorName());
            if(user != null){
                finder.byAuthor(user);
            }
        }
        if(form.isOnlyFollow() && currentUser.isLoggedIn()){
            finder.joinUserFollows(genericRepository.findById(User.class,currentUser.getId()));
        }
        if(form.getPostedAfter() != null){
            finder.postedAfter(form.getPostedAfter());
        }
        if(form.getPostedBefore() != null){
            finder.postedBefore(form.getPostedBefore());
        }

        //null-safe switch
        switch(form.getOrderBy() != null ? form.getOrderBy() : NEWEST){
            case NEWEST: default: {
                finder.getNewest();
                break;
            }
            case OLDEST:{
                finder.getOldest();
                break;
            }
            case MOSTVOTED:{
                finder.getMostVoted();
                break;
            }
        }
        return finder.getResults().stream().map(this::mapPost).collect(Collectors.toList());
    }

    /**
     * Elimina un post dato il suo id
     * @param id identificativo del post
     */
    @AuthenticationRequired //TODO: check autore post?
    @DenyBannedUsers
    public void delete(@PostExists int id){
        Post post = genericRepository.findById(Post.class,id);
        if(currentUser.getId() != post.getAuthor().getId() && !currentUser.isAdmin())
            throw new AuthorizationException();
        genericRepository.remove(genericRepository.findById(User.class,id));
    }

    /**
     * Aggiunge un post ad una sezione
     * @param title titolo del post
     * @param body testo del post
     * @param sectionName nome della sezione
     * @return identificativo del nuovo post creato
     */
    @AuthenticationRequired
    @DenyBannedUsers
    public int newPost(@NotBlank String title,
                       @Size(max=65535) String body,
                       @NotNull @SectionExists String sectionName){
        User user = genericRepository.findByNaturalId(User.class, currentUser.getUsername());
        Section section = genericRepository.findByNaturalId(Section.class,sectionName);


        Post post = new Post();
        post.setTitle(title);
        post.setContent(body == null || body.isBlank() ? "" : body);
        post.setAuthor(user);
        post.setSection(section);
        post.setType(TEXT);

        return genericRepository.insert(post).getId();
    }

    /**
     * Aggiunge un post ad una sezione
     * @param title titolo del post
     * @param content file di tipo immagine
     * @param sectionName nome della sezione
     * @return identificativo del nuovo post creato
     */
    @AuthenticationRequired
    @DenyBannedUsers
    public int newPost(@NotBlank @Size(max=255) String title,
                       @NotNull @Image BufferedInputStream content,
                       @NotNull @SectionExists String sectionName){
        User user = genericRepository.findByNaturalId(User.class,currentUser.getUsername());
        Section section = genericRepository.findByNaturalId(Section.class,sectionName);

        String fileName;
        try {
            fileName = bcRepo.insert(content);
        } catch (ReadLimitExceededException e){
            throw new IllegalArgumentException("Il file non deve superare i 5MB");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        Post post = new Post();
        post.setTitle(title);
        post.setAuthor(user);
        post.setContent(fileName);
        post.setSection(section);
        post.setType(IMG);

        return genericRepository.insert(post).getId(); //in caso di errore il file resta
    }
}