com.qatickets.service.TicketService.java Source code

Java tutorial

Introduction

Here is the source code for com.qatickets.service.TicketService.java

Source

/*
The MIT License (MIT)
    
Copyright (c) 2015 QAXML Pty Ltd
    
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
    
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
    
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.qatickets.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.qatickets.dao.TicketBodyDao;
import com.qatickets.dao.TicketComponentDao;
import com.qatickets.dao.TicketDao;
import com.qatickets.dao.TicketWatcherDao;
import com.qatickets.domain.Comment;
import com.qatickets.domain.Component;
import com.qatickets.domain.Link;
import com.qatickets.domain.Project;
import com.qatickets.domain.Ticket;
import com.qatickets.domain.TicketBody;
import com.qatickets.domain.TicketComponent;
import com.qatickets.domain.TicketFileAttachment;
import com.qatickets.domain.TicketWatcher;
import com.qatickets.domain.UserProfile;
import com.qatickets.domain.Version;
import com.qatickets.search.SearchService;
import com.qatickets.search.model.SearchIndexableEntity;

@Service
public class TicketService {

    private static final Log log = LogFactory.getLog(TicketService.class);

    @Inject
    private TicketDao dao;

    @Inject
    private LinkService linkService;

    @Inject
    private TicketBodyDao bodyDao;

    @Inject
    private ProjectService projectService;

    @Inject
    private VersionService versionService;

    @Inject
    private UserService userService;

    @Inject
    private ComponentService componentService;

    @Inject
    private SpamService spamService;

    @Inject
    private SearchService searchService;

    @Inject
    private TicketActivityEventService eventService;

    @Inject
    private TicketWatcherDao watcherDao;

    @Inject
    private TicketFileAttachmentService attachmentService;

    @Inject
    private TicketComponentDao ticketComponentDao;

    @Transactional(readOnly = true)
    public List<Ticket> findByProject(Project project, Pageable pageable) {
        log.debug("Get bugs for: " + project);
        List<Ticket> list = dao.findByProject(project, pageable);
        log.debug("Found bugs: " + list.size());
        return list;
    }

    @Transactional(readOnly = false)
    public void save(Ticket t) {
        log.debug("Save Ticket: " + t);
        this.dao.save(t);
    }

    @Transactional(readOnly = true)
    public Ticket findById(int id) {
        return dao.findOne(id);
    }

    @Transactional(readOnly = true)
    public List<Comment> findCommentsByTicketId(int ticketId) {
        List<Comment> comments = null;
        Ticket ticket = this.findById(ticketId);
        if (ticket != null) {
            comments = new ArrayList<Comment>(ticket.getComments());

            Collections.sort(comments, new Comparator<Comment>() {
                @Override
                public int compare(Comment c1, Comment c2) {
                    return c1.getDate().compareTo(c2.getDate());
                }
            });
        }
        return comments;
    }

    @Transactional(readOnly = false)
    public void resetAssignee(int userId) {
        dao.resetAssignee(userId);
    }

    @Transactional(readOnly = false)
    public void resetReporter(int userId) {
        dao.resetReporter(userId);
    }

    @Transactional(readOnly = false)
    public void removeFixVersion(int versionId) {
        dao.removeFixVersion(versionId);
    }

    @Transactional(readOnly = false)
    public void removeFoundVersion(int versionId) {
        dao.removeFoundVersion(versionId);
    }

    @Transactional(readOnly = false)
    public Ticket saveNew(UserProfile user, int projectId, String summary, int type, int status, int businessImpact,
            int foundInVersion, int fixInVersion, int assigneeId, int reporterId, String description,
            String environment, String components, Integer parent) {

        log.debug("Save new ticket to " + projectId);

        // get num
        Project project = projectService.findById(projectId);

        // check if project belongs to and user is a project user
        if (project == null) {
            return null;
        }

        // clean summary
        summary = spamService.clean(summary, 256);

        // clean description
        description = spamService.clean(description, 40000);

        // clean environment
        environment = spamService.clean(environment, 128);

        // validate input values
        if (false == Ticket.isValidTypeValue(type)) {
            return null;
        }
        if (false == Ticket.isValidBusinessImpactValue(businessImpact)) {
            return null;
        }
        if (false == Ticket.isValidStatusValue(status)) {
            return null;
        }

        // get ticket number
        int num = project.getLastNum();
        project.setLastNum(num + 1);
        this.projectService.save(project);

        // create new ticket
        Ticket ticket = new Ticket();

        // parent
        if (parent != null && parent > 0) {
            Ticket parentTicket = this.dao.findOne(parent);
            ticket.setParent(parentTicket);
        }

        // load assignee and reporter
        UserProfile assignee = this.userService.findById(assigneeId);
        UserProfile reporter = this.userService.findById(reporterId);

        // load versions
        Version fixScheduledVersion = versionService.findByProjectAndId(project, fixInVersion);
        Version foundVersion = versionService.findByProjectAndId(project, foundInVersion);

        ticket.setNum(project.getIdentifier() + "-" + num);
        ticket.setAssignee(assignee);
        ticket.setBusinessImpact(businessImpact);
        ticket.setCreateDate(new Date());
        ticket.setFixScheduledVersion(fixScheduledVersion);
        ticket.setFoundVersion(foundVersion);

        ticket.setProject(project);
        ticket.setReporter(reporter);
        ticket.setStatus(status);
        ticket.setSummary(summary);

        if (reporter != null && assignee != null && false == reporter.equals(assignee)) {
            ticket.addWatcher(new TicketWatcher(ticket, reporter));
            ticket.addWatcher(new TicketWatcher(ticket, assignee));
        } else {
            ticket.addWatcher(new TicketWatcher(ticket, assignee == null ? reporter : assignee));
        }

        TicketBody body = new TicketBody();
        body.setTicket(ticket);
        body.setDescription(description);
        body.setEnvironment(environment);

        // components
        if (StringUtils.isNotBlank(components)) {
            String[] split = components.split(",");
            if (split != null && split.length > 0) {
                for (String id : split) {
                    id = id.trim();
                    if (NumberUtils.isDigits(id)) {
                        int componentId = Integer.parseInt(id);
                        Component component = this.componentService.findByProjectAndId(project, componentId);
                        if (component != null) {
                            ticket.addComponent(new TicketComponent(ticket, component));
                        }
                    }
                }
            }
        }

        bodyDao.save(body);

        ticket.setTicketBody(body);
        ticket.setType(type);
        ticket.setUpdateDate(new Date());
        ticket.setUser(user);

        this.save(ticket);

        log.debug("Ticket saved: " + ticket);

        body.setTicket(ticket);
        bodyDao.save(body);

        // save to SOLR
        SearchIndexableEntity entity = new SearchIndexableEntity();
        entity.setProject(project.getId());
        entity.setPk(ticket.getId());
        entity.setText(ticket.getSearchableText());
        entity.setDate(ticket.getCreateDate());
        entity.setType(SearchService.Type.TICKET.name());
        searchService.save(entity);

        // create ticket activity event
        eventService.saveNewTicket(ticket);

        return ticket;
    }

    @Transactional(readOnly = false)
    public void updateSummary(UserProfile user, int pk, String summary) {

        log.debug("Update summary to '" + summary + "' for PK: " + pk);

        final Ticket ticket = this.dao.findOne(pk);

        String oldSummary = StringUtils.EMPTY;

        if (ticket != null) {

            oldSummary = ticket.getSummary();

            summary = spamService.clean(summary, 256);
            ticket.setSummary(summary);
            this.save(ticket);

            // event
            if (false == StringUtils.equals(summary, oldSummary)) {
                eventService.updateTicketSummary(user, ticket, oldSummary);
            }
        }

    }

    @Transactional(readOnly = false)
    public Ticket updateStatus(UserProfile user, int id, int value) {

        log.debug("Update status to '" + value + "' for PK: " + id);

        final Ticket ticket = this.dao.findOne(id);

        if (ticket != null) {

            if (Ticket.isValidStatusValue(value)) {

                int oldStatus = ticket.getStatus();

                ticket.setStatus(value);
                this.save(ticket);

                // event
                if (oldStatus != ticket.getStatus()) {
                    eventService.updateTicketStatus(user, ticket, oldStatus);
                }
            }
        }

        return ticket;
    }

    @Transactional(readOnly = true)
    public List<Ticket> findByProjectOrderByCreateDateDesc(Integer projectId, PageRequest pageable) {
        final Project project = this.projectService.findById(projectId);
        return dao.findByProjectOrderByCreateDateDesc(project, pageable);
    }

    @Transactional(readOnly = true)
    public List<Ticket> findByProjectAndAssigneeOrderByCreateDateDesc(Integer project, UserProfile user,
            PageRequest pageable) {
        return dao.findByProjectAndAssigneeOrderByCreateDateDesc(project, user, pageable);
    }

    @Transactional(readOnly = true)
    public List<Ticket> findByProjectAndStatusIn(Project project, List<Integer> statuses) {
        return dao.findByProjectAndStatusIn(project, statuses);
    }

    @Transactional(readOnly = true)
    public int countOpenedByProjectAndAssignee(Project project, UserProfile user) {
        return dao.countByProjectAndAssigneeAndStatusIn(project, user, Ticket.OPENED_STATUSES);
    }

    @Transactional(readOnly = true)
    public int countOpenedByProjectAndReporter(Project project, UserProfile user) {
        return dao.countByProjectAndReporterAndStatusIn(project, user, Ticket.OPENED_STATUSES);
    }

    @Transactional(readOnly = true)
    public int countOpenedByAssignee(UserProfile user) {
        return dao.countByAssigneeAndStatusIn(user, Ticket.OPENED_STATUSES);
    }

    @Transactional(readOnly = true)
    public int countOpenedByReporter(UserProfile user) {
        return dao.countByReporterAndStatusIn(user, Ticket.OPENED_STATUSES);
    }

    @Transactional(readOnly = true)
    public int countByProjectAndStatus(Project project, int status) {
        return dao.countByProjectAndStatus(project, status);
    }

    @Transactional(readOnly = true)
    public int countByProject(Project project) {
        return dao.countByProject(project);
    }

    @Transactional(readOnly = false)
    public Ticket updateType(UserProfile user, int id, int value) {

        log.debug("Update type to '" + value + "' for PK: " + id);

        final Ticket ticket = this.dao.findOne(id);

        if (ticket != null) {
            if (Ticket.isValidTypeValue(value)) {

                int oldType = ticket.getType();

                ticket.setType(value);
                this.save(ticket);

                // event
                if (oldType != ticket.getType()) {
                    eventService.updateTicketType(user, ticket, oldType);
                }
            }
        }
        return ticket;

    }

    @Transactional(readOnly = false)
    public Ticket updateBusinessImpact(UserProfile user, int id, int value) {

        log.debug("Update business impact to '" + value + "' for PK: " + id);

        final Ticket ticket = this.dao.findOne(id);

        if (ticket != null) {
            if (Ticket.isValidBusinessImpactValue(value)) {

                final int oldImpact = ticket.getBusinessImpact();

                ticket.setBusinessImpact(value);
                this.save(ticket);

                // event
                if (oldImpact != ticket.getBusinessImpact()) {
                    eventService.updateTicketBusinessImpact(user, ticket, oldImpact);
                }
            }
        }
        return ticket;
    }

    @Transactional(readOnly = true)
    public List<TicketFileAttachment> findAttachmentsByTicketId(int ticketId) {

        List<TicketFileAttachment> list = null;
        Ticket ticket = this.findById(ticketId);
        if (ticket != null) {
            list = new ArrayList<TicketFileAttachment>(ticket.getFileAttachments());

            Collections.sort(list, new Comparator<TicketFileAttachment>() {
                @Override
                public int compare(TicketFileAttachment c1, TicketFileAttachment c2) {
                    return c1.getDate().compareTo(c2.getDate());
                }
            });
        }
        return list;

    }

    @Transactional(readOnly = true)
    public List<Ticket> findAllByNumLike(String query, Integer page) {
        query = query.toUpperCase();
        Pageable pageable = new PageRequest(0, page);
        // return dao.findByNumContaining(query, pageable);
        return null;
    }

    @Transactional(readOnly = true)
    public List<Ticket> findAllByIds(List<Integer> ids) {
        return dao.findAll(ids);
    }

    @Transactional(readOnly = true)
    public List<Ticket> findByParent(Ticket parent) {
        if (parent == null) {
            return Collections.<Ticket>emptyList();
        }
        return dao.findByParent(parent);
    }

    @Transactional(readOnly = false)
    public void updateReporter(UserProfile user, int ticketId, int reporterId) {

        log.debug("Update ticket '" + ticketId + "' to reporter: " + reporterId);

        final Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            UserProfile oldReporter = ticket.getReporter();

            UserProfile newReporter = this.userService.findById(reporterId);

            ticket.setReporter(newReporter);

            this.save(ticket);

            // event
            eventService.updateReporter(user, ticket, oldReporter, newReporter);
        }

    }

    @Transactional(readOnly = false)
    public void updateAssignee(UserProfile user, int ticketId, int assigneeId) {

        log.debug("Update ticket '" + ticketId + "' to assignee: " + assigneeId);

        final Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            UserProfile oldAssignee = ticket.getAssignee();

            UserProfile newAssignee = this.userService.findById(assigneeId);

            ticket.setAssignee(newAssignee);

            this.save(ticket);

            // event
            eventService.updateAssignee(user, ticket, oldAssignee, newAssignee);
        }

    }

    @Transactional(readOnly = false)
    public void updateVersionFound(UserProfile user, int ticketId, int versionId) {

        log.debug("Update ticket '" + ticketId + "' to version found: " + versionId);

        final Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            Version oldVersion = ticket.getFoundVersion();

            Version newVersion = this.versionService.findByProjectAndId(ticket.getProject(), versionId);

            ticket.setFoundVersion(newVersion);

            this.save(ticket);

            // event
            eventService.updateFoundVersion(user, ticket, oldVersion, newVersion);
        }

    }

    @Transactional(readOnly = false)
    public void updateVersionFix(UserProfile user, int ticketId, int versionId) {

        log.debug("Update ticket '" + ticketId + "' to version fix: " + versionId);

        final Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            Version oldVersion = ticket.getFixScheduledVersion();

            Version newVersion = this.versionService.findByProjectAndId(ticket.getProject(), versionId);

            ticket.setFixScheduledVersion(newVersion);

            this.save(ticket);

            // event
            eventService.updateFixVersion(user, ticket, oldVersion, newVersion);
        }

    }

    @Transactional(readOnly = false)
    public void removeSubTicket(UserProfile user, int ticketId, int subTicketId) {

        log.debug("Remove subticket from " + ticketId + "' by PK: " + subTicketId);

        final Ticket ticket = this.findById(ticketId);
        final Ticket subticket = this.findById(subTicketId);

        if (ticket != null && subticket != null) {

            subticket.setParent(null);

            this.save(subticket);

            // event
            eventService.removedSubTicket(user, ticket, subticket);
        }

    }

    @Transactional(readOnly = false)
    public void addSubTicket(UserProfile user, int ticketId, int subTicketId) {

        log.debug("Add subticket to " + ticketId + "' by PK: " + subTicketId);

        final Ticket ticket = this.findById(ticketId);
        final Ticket subticket = this.findById(subTicketId);

        if (ticket != null && subticket != null) {

            subticket.setParent(ticket);

            this.save(subticket);

            // event
            eventService.addedSubTicket(user, ticket, subticket);
        }

    }

    @Transactional(readOnly = false)
    public void removeParent(UserProfile user, int ticketId) {

        log.debug("Remove parent from " + ticketId);

        final Ticket ticket = this.findById(ticketId);
        final Ticket parent = ticket.getParent();

        if (ticket != null && parent != null) {

            ticket.setParent(null);

            this.save(ticket);

            // event
            eventService.removedSubTicket(user, parent, ticket);
        }
    }

    @Transactional(readOnly = false)
    public void updateDescription(UserProfile user, int ticketId, String description) {

        log.debug("updateDescription " + ticketId);

        final Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            String oldDesc = ticket.getTicketBody().getDescription();

            description = spamService.clean(description, 40000);

            ticket.getTicketBody().setDescription(description);

            this.bodyDao.save(ticket.getTicketBody());

            // event
            eventService.updatedDesc(user, ticket, oldDesc, description);
        }

    }

    @Transactional(readOnly = true)
    public List<TicketWatcher> findWatchersByTicket(Ticket t) {
        return this.watcherDao.findByTicket(t);
    }

    @Transactional(readOnly = false)
    public void stopWatching(UserProfile user, Ticket t) {
        TicketWatcher w = this.watcherDao.findByTicketAndUser(t, user);
        if (w != null) {
            this.watcherDao.delete(w);
        }
    }

    @Transactional(readOnly = false)
    public void startWatching(UserProfile user, Ticket t) {
        TicketWatcher w = this.watcherDao.findByTicketAndUser(t, user);
        if (w == null) {
            w = new TicketWatcher(t, user);
            this.watcherDao.save(w);
        }
    }

    @Transactional(readOnly = false)
    public void updateProgress(UserProfile user, int ticketId, int progress) {

        Ticket ticket = this.findById(ticketId);

        if (ticket != null) {

            if (progress < 0) {
                progress = 0;
            }
            if (progress > 100) {
                progress = 100;
            }

            int oldProgress = ticket.getProgress();

            ticket.setProgress(progress);
            this.save(ticket);

            // event
            this.eventService.updateProgress(user, ticket, oldProgress, progress);
        }
    }

    @Transactional(readOnly = false)
    public void addLink(UserProfile user, String linkTicketId, int ticketId) {

        Ticket ticket = this.findById(ticketId);
        Ticket link = this.findById(ticketId);

        if (ticket != null && link != null && ticket.equals(link) == false) {
            Link l = new Link();
            l.setDate(new Date());
            l.setSource(ticket);
            l.setDestination(link);
            l.setType(Link.LinkType.REL);
            l.setUser(user);
            this.linkService.save(l);

            // event
            // this.eventService.addedLink(user, ticket, oldProgress, progress);
        }
    }

    @Transactional(readOnly = false)
    public void updateComponents(UserProfile user, int projectId, int ticketId, Integer[] components) {

        log.debug("Update components for ticket: " + ticketId + " to '" + components + "'");

        final Ticket ticket = this.findById(ticketId);
        final Project project = this.projectService.findById(projectId);

        if (ticket == null || project == null) {
            return;
        }

        // delete existing ticket components
        ticketComponentDao.deleteByTicket(ticket);

        final Set<TicketComponent> list = new HashSet<TicketComponent>();
        ticket.setComponents(list);

        if (!ArrayUtils.isEmpty(components)) {
            for (Integer componentId : components) {
                final Component component = this.componentService.findByProjectAndId(project, componentId);
                if (component != null) {
                    log.debug("Add component: " + component.getName() + " to ticket: " + ticket.getNum());
                    ticket.addComponent(new TicketComponent(ticket, component));
                }
            }
        }

        this.save(ticket);

    }

    @Transactional(readOnly = false)
    public void deleteComponent(UserProfile user, int ticketId, int componentId) {

        log.debug("Delete component from ticket: " + ticketId + " '" + componentId + "'");

        final Ticket ticket = this.findById(ticketId);

        if (ticket == null) {
            return;
        }

        ticketComponentDao.deleteComponentFromTicket(ticket, componentId);

    }

    @Transactional(readOnly = false)
    public void deleteTicket(int id) {

        // delete from search engine
        this.searchService.deleteByTicket(id);

        // delete links
        this.linkService.deleteByTicket(id);

        // delete attachments
        // this.attachmentService.deleteByTicket(id);

        this.dao.delete(id);

    }

    @Transactional(readOnly = true)
    public List<Integer> findIdsByProjectId(int id) {
        return dao.findIdsByProjectId(id);
    }

}