Search in sources :

Example 16 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class TicketNotifier method formatLastChange.

protected String formatLastChange(TicketModel ticket) {
    Change lastChange = ticket.changes.get(ticket.changes.size() - 1);
    UserModel user = getUserModel(lastChange.author);
    // define the fields we do NOT want to see in an email notification
    Set<TicketModel.Field> fieldExclusions = new HashSet<TicketModel.Field>();
    fieldExclusions.addAll(Arrays.asList(Field.watchers, Field.voters));
    StringBuilder sb = new StringBuilder();
    boolean newTicket = lastChange.isStatusChange() && Status.New == lastChange.getStatus();
    boolean isFastForward = true;
    List<RevCommit> commits = null;
    DiffStat diffstat = null;
    String pattern;
    if (lastChange.hasPatchset()) {
        // patchset uploaded
        Patchset patchset = lastChange.patchset;
        String base = "";
        // determine the changed paths
        Repository repo = null;
        try {
            repo = repositoryManager.getRepository(ticket.repository);
            if (patchset.isFF() && (patchset.rev > 1)) {
                // fast-forward update, just show the new data
                isFastForward = true;
                Patchset prev = ticket.getPatchset(patchset.number, patchset.rev - 1);
                base = prev.tip;
            } else {
                // proposal OR non-fast-forward update
                isFastForward = false;
                base = patchset.base;
            }
            diffstat = DiffUtils.getDiffStat(repo, base, patchset.tip);
            commits = JGitUtils.getRevLog(repo, base, patchset.tip);
        } catch (Exception e) {
            Logger.getLogger(getClass()).error("failed to get changed paths", e);
        } finally {
            if (repo != null) {
                repo.close();
            }
        }
        String compareUrl = ticketService.getCompareUrl(ticket, base, patchset.tip);
        if (newTicket) {
            // new proposal
            pattern = "**{0}** is proposing a change.";
            sb.append(MessageFormat.format(pattern, user.getDisplayName()));
            fieldExclusions.add(Field.status);
            fieldExclusions.add(Field.title);
            fieldExclusions.add(Field.body);
        } else {
            // describe the patchset
            if (patchset.isFF()) {
                pattern = "**{0}** added {1} {2} to patchset {3}.";
                sb.append(MessageFormat.format(pattern, user.getDisplayName(), patchset.added, patchset.added == 1 ? "commit" : "commits", patchset.number));
            } else {
                pattern = "**{0}** uploaded patchset {1}. *({2})*";
                sb.append(MessageFormat.format(pattern, user.getDisplayName(), patchset.number, patchset.type.toString().toUpperCase()));
            }
        }
        sb.append(HARD_BRK);
        sb.append(MessageFormat.format("{0} {1}, {2} {3}, <span style=\"color:darkgreen;\">+{4} insertions</span>, <span style=\"color:darkred;\">-{5} deletions</span> from {6}. [compare]({7})", commits.size(), commits.size() == 1 ? "commit" : "commits", diffstat.paths.size(), diffstat.paths.size() == 1 ? "file" : "files", diffstat.getInsertions(), diffstat.getDeletions(), isFastForward ? "previous revision" : "merge base", compareUrl));
        // note commit additions on a rebase,if any
        switch(lastChange.patchset.type) {
            case Rebase:
                if (lastChange.patchset.added > 0) {
                    sb.append(SOFT_BRK);
                    sb.append(MessageFormat.format("{0} {1} added.", lastChange.patchset.added, lastChange.patchset.added == 1 ? "commit" : "commits"));
                }
                break;
            default:
                break;
        }
        sb.append(HARD_BRK);
    } else if (lastChange.isStatusChange()) {
        if (newTicket) {
            fieldExclusions.add(Field.status);
            fieldExclusions.add(Field.title);
            fieldExclusions.add(Field.body);
            pattern = "**{0}** created this ticket.";
            sb.append(MessageFormat.format(pattern, user.getDisplayName()));
        } else if (lastChange.hasField(Field.mergeSha)) {
            // closed by merged
            pattern = "**{0}** closed this ticket by merging {1} to {2}.";
            // identify patchset that closed the ticket
            String merged = ticket.mergeSha;
            for (Patchset patchset : ticket.getPatchsets()) {
                if (patchset.tip.equals(ticket.mergeSha)) {
                    merged = patchset.toString();
                    break;
                }
            }
            sb.append(MessageFormat.format(pattern, user.getDisplayName(), merged, ticket.mergeTo));
        } else {
            // workflow status change by user
            pattern = "**{0}** changed the status of this ticket to **{1}**.";
            sb.append(MessageFormat.format(pattern, user.getDisplayName(), lastChange.getStatus().toString().toUpperCase()));
        }
        sb.append(HARD_BRK);
    } else if (lastChange.hasReview()) {
        // review
        Review review = lastChange.review;
        pattern = "**{0}** has reviewed patchset {1,number,0} revision {2,number,0}.";
        sb.append(MessageFormat.format(pattern, user.getDisplayName(), review.patchset, review.rev));
        sb.append(HARD_BRK);
        String d = settings.getString(Keys.web.datestampShortFormat, "yyyy-MM-dd");
        String t = settings.getString(Keys.web.timeFormat, "HH:mm");
        DateFormat df = new SimpleDateFormat(d + " " + t);
        List<Change> reviews = ticket.getReviews(ticket.getPatchset(review.patchset, review.rev));
        sb.append("| Date | Reviewer      | Score | Description  |\n");
        sb.append("| :--- | :------------ | :---: | :----------- |\n");
        for (Change change : reviews) {
            String name = change.author;
            UserModel u = userManager.getUserModel(change.author);
            if (u != null) {
                name = u.getDisplayName();
            }
            String score;
            switch(change.review.score) {
                case approved:
                    score = MessageFormat.format(addPattern, change.review.score.getValue());
                    break;
                case vetoed:
                    score = MessageFormat.format(delPattern, Math.abs(change.review.score.getValue()));
                    break;
                default:
                    score = "" + change.review.score.getValue();
            }
            String date = df.format(change.date);
            sb.append(String.format("| %1$s | %2$s | %3$s | %4$s |\n", date, name, score, change.review.score.toString()));
        }
        sb.append(HARD_BRK);
    } else if (lastChange.hasComment()) {
        // comment update
        sb.append(MessageFormat.format("**{0}** commented on this ticket.", user.getDisplayName()));
        sb.append(HARD_BRK);
    } else if (lastChange.hasReference()) {
        // reference update
        String type = "?";
        switch(lastChange.reference.getSourceType()) {
            case Commit:
                {
                    type = "commit";
                }
                break;
            case Ticket:
                {
                    type = "ticket";
                }
                break;
            default:
                {
                }
                break;
        }
        sb.append(MessageFormat.format("**{0}** referenced this ticket in {1} {2}", type, lastChange.toString()));
        sb.append(HARD_BRK);
    } else {
        // general update
        pattern = "**{0}** has updated this ticket.";
        sb.append(MessageFormat.format(pattern, user.getDisplayName()));
        sb.append(HARD_BRK);
    }
    // ticket link
    sb.append(MessageFormat.format("[view ticket {0,number,0}]({1})", ticket.number, ticketService.getTicketUrl(ticket)));
    sb.append(HARD_BRK);
    if (newTicket) {
        // ticket title
        sb.append(MessageFormat.format("### {0}", ticket.title));
        sb.append(HARD_BRK);
        // ticket description, on state change
        if (StringUtils.isEmpty(ticket.body)) {
            sb.append("<span style=\"color: #888;\">no description entered</span>");
        } else {
            sb.append(ticket.body);
        }
        sb.append(HARD_BRK);
        sb.append(HR);
    }
    // field changes
    if (lastChange.hasFieldChanges()) {
        Map<Field, String> filtered = new HashMap<Field, String>();
        for (Map.Entry<Field, String> fc : lastChange.fields.entrySet()) {
            if (!fieldExclusions.contains(fc.getKey())) {
                // field is included
                filtered.put(fc.getKey(), fc.getValue());
            }
        }
        // sort by field ordinal
        List<Field> fields = new ArrayList<Field>(filtered.keySet());
        Collections.sort(fields);
        if (filtered.size() > 0) {
            sb.append(HARD_BRK);
            sb.append("| Field Changes               ||\n");
            sb.append("| ------------: | :----------- |\n");
            for (Field field : fields) {
                String value;
                if (filtered.get(field) == null) {
                    value = "";
                } else {
                    value = filtered.get(field).replace("\r\n", "<br/>").replace("\n", "<br/>").replace("|", "&#124;");
                }
                sb.append(String.format("| **%1$s:** | %2$s |\n", field.name(), value));
            }
            sb.append(HARD_BRK);
        }
    }
    // new comment
    if (lastChange.hasComment()) {
        sb.append(HR);
        sb.append(lastChange.comment.text);
        sb.append(HARD_BRK);
    }
    // insert the patchset details and review instructions
    if (lastChange.hasPatchset() && ticket.isOpen()) {
        if (commits != null && commits.size() > 0) {
            // append the commit list
            String title = isFastForward ? "Commits added to previous patchset revision" : "All commits in patchset";
            sb.append(MessageFormat.format("| {0} |||\n", title));
            sb.append("| SHA | Author | Title |\n");
            sb.append("| :-- | :----- | :---- |\n");
            for (RevCommit commit : commits) {
                sb.append(MessageFormat.format("| {0} | {1} | {2} |\n", commit.getName(), commit.getAuthorIdent().getName(), StringUtils.trimString(commit.getShortMessage(), Constants.LEN_SHORTLOG).replace("|", "&#124;")));
            }
            sb.append(HARD_BRK);
        }
        if (diffstat != null) {
            // append the changed path list
            String title = isFastForward ? "Files changed since previous patchset revision" : "All files changed in patchset";
            sb.append(MessageFormat.format("| {0} |||\n", title));
            sb.append("| :-- | :----------- | :-: |\n");
            for (PathChangeModel path : diffstat.paths) {
                String add = MessageFormat.format(addPattern, path.insertions);
                String del = MessageFormat.format(delPattern, path.deletions);
                String diff = null;
                switch(path.changeType) {
                    case ADD:
                        diff = add;
                        break;
                    case DELETE:
                        diff = del;
                        break;
                    case MODIFY:
                        if (path.insertions > 0 && path.deletions > 0) {
                            // insertions & deletions
                            diff = add + "/" + del;
                        } else if (path.insertions > 0) {
                            // just insertions
                            diff = add;
                        } else {
                            // just deletions
                            diff = del;
                        }
                        break;
                    default:
                        diff = path.changeType.name();
                        break;
                }
                sb.append(MessageFormat.format("| {0} | {1} | {2} |\n", getChangeType(path.changeType), path.name, diff));
            }
            sb.append(HARD_BRK);
        }
        sb.append(formatPatchsetInstructions(ticket, lastChange.patchset));
    }
    return sb.toString();
}
Also used : HashMap(java.util.HashMap) DiffStat(com.gitblit.utils.DiffUtils.DiffStat) ArrayList(java.util.ArrayList) Patchset(com.gitblit.models.TicketModel.Patchset) Review(com.gitblit.models.TicketModel.Review) UserModel(com.gitblit.models.UserModel) Field(com.gitblit.models.TicketModel.Field) HashSet(java.util.HashSet) RevCommit(org.eclipse.jgit.revwalk.RevCommit) PathChangeModel(com.gitblit.models.PathModel.PathChangeModel) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change) IOException(java.io.IOException) Repository(org.eclipse.jgit.lib.Repository) SimpleDateFormat(java.text.SimpleDateFormat) DateFormat(java.text.DateFormat) SimpleDateFormat(java.text.SimpleDateFormat) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap)

Example 17 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class BranchTicketService method getTicketImpl.

/**
	 * Retrieves the ticket from the repository by first looking-up the changeId
	 * associated with the ticketId.
	 *
	 * @param repository
	 * @param ticketId
	 * @return a ticket, if it exists, otherwise null
	 */
@Override
protected TicketModel getTicketImpl(RepositoryModel repository, long ticketId) {
    Repository db = repositoryManager.getRepository(repository.name);
    try {
        List<Change> changes = getJournal(db, ticketId);
        if (ArrayUtils.isEmpty(changes)) {
            log.warn("Empty journal for {}:{}", repository, ticketId);
            return null;
        }
        TicketModel ticket = TicketModel.buildTicket(changes);
        if (ticket != null) {
            ticket.project = repository.projectPath;
            ticket.repository = repository.name;
            ticket.number = ticketId;
        }
        return ticket;
    } finally {
        db.close();
    }
}
Also used : Repository(org.eclipse.jgit.lib.Repository) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change)

Example 18 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class BranchTicketService method getTickets.

/**
	 * Returns all the tickets in the repository. Querying tickets from the
	 * repository requires deserializing all tickets. This is an  expensive
	 * process and not recommended. Tickets are indexed by Lucene and queries
	 * should be executed against that index.
	 *
	 * @param repository
	 * @param filter
	 *            optional filter to only return matching results
	 * @return a list of tickets
	 */
@Override
public List<TicketModel> getTickets(RepositoryModel repository, TicketFilter filter) {
    List<TicketModel> list = new ArrayList<TicketModel>();
    Repository db = repositoryManager.getRepository(repository.name);
    try {
        RefModel ticketsBranch = getTicketsBranch(db);
        if (ticketsBranch == null) {
            return list;
        }
        // Collect the set of all json files
        List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH);
        // Deserialize each ticket and optionally filter out unwanted tickets
        for (PathModel path : paths) {
            String name = path.name.substring(path.name.lastIndexOf('/') + 1);
            if (!JOURNAL.equals(name)) {
                continue;
            }
            String json = readTicketsFile(db, path.path);
            if (StringUtils.isEmpty(json)) {
                // journal was touched but no changes were written
                continue;
            }
            try {
                // Reconstruct ticketId from the path
                // id/26/326/journal.json
                String tid = path.path.split("/")[2];
                long ticketId = Long.parseLong(tid);
                List<Change> changes = TicketSerializer.deserializeJournal(json);
                if (ArrayUtils.isEmpty(changes)) {
                    log.warn("Empty journal for {}:{}", repository, path.path);
                    continue;
                }
                TicketModel ticket = TicketModel.buildTicket(changes);
                ticket.project = repository.projectPath;
                ticket.repository = repository.name;
                ticket.number = ticketId;
                // add the ticket, conditionally, to the list
                if (filter == null) {
                    list.add(ticket);
                } else {
                    if (filter.accept(ticket)) {
                        list.add(ticket);
                    }
                }
            } catch (Exception e) {
                log.error("failed to deserialize {}/{}\n{}", new Object[] { repository, path.path, e.getMessage() });
                log.error(null, e);
            }
        }
        // sort the tickets by creation
        Collections.sort(list);
        return list;
    } finally {
        db.close();
    }
}
Also used : RefModel(com.gitblit.models.RefModel) ArrayList(java.util.ArrayList) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change) ConcurrentRefUpdateException(org.eclipse.jgit.api.errors.ConcurrentRefUpdateException) IOException(java.io.IOException) Repository(org.eclipse.jgit.lib.Repository) PathModel(com.gitblit.models.PathModel)

Example 19 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class RedisTicketService method commitChangeImpl.

/**
	 * Commit a ticket change to the repository.
	 *
	 * @param repository
	 * @param ticketId
	 * @param change
	 * @return true, if the change was committed
	 */
@Override
protected boolean commitChangeImpl(RepositoryModel repository, long ticketId, Change change) {
    Jedis jedis = pool.getResource();
    if (jedis == null) {
        return false;
    }
    try {
        List<Change> changes = getJournal(jedis, repository, ticketId);
        changes.add(change);
        // build a new effective ticket from the changes
        TicketModel ticket = TicketModel.buildTicket(changes);
        String object = TicketSerializer.serialize(ticket);
        String journal = TicketSerializer.serialize(change);
        // atomically store ticket
        Transaction t = jedis.multi();
        t.set(key(repository, KeyType.ticket, ticketId), object);
        t.rpush(key(repository, KeyType.journal, ticketId), journal);
        t.exec();
        log.debug("updated ticket {} in Redis @ {}", "" + ticketId, getUrl());
        return true;
    } catch (JedisException e) {
        log.error("failed to update ticket cache in Redis @ " + getUrl(), e);
        pool.returnBrokenResource(jedis);
        jedis = null;
    } finally {
        if (jedis != null) {
            pool.returnResource(jedis);
        }
    }
    return false;
}
Also used : Jedis(redis.clients.jedis.Jedis) JedisException(redis.clients.jedis.exceptions.JedisException) Transaction(redis.clients.jedis.Transaction) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change)

Example 20 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class RedisTicketService method getTickets.

/**
	 * Returns all the tickets in the repository. Querying tickets from the
	 * repository requires deserializing all tickets. This is an  expensive
	 * process and not recommended. Tickets should be indexed by Lucene and
	 * queries should be executed against that index.
	 *
	 * @param repository
	 * @param filter
	 *            optional filter to only return matching results
	 * @return a list of tickets
	 */
@Override
public List<TicketModel> getTickets(RepositoryModel repository, TicketFilter filter) {
    Jedis jedis = pool.getResource();
    List<TicketModel> list = new ArrayList<TicketModel>();
    if (jedis == null) {
        return list;
    }
    try {
        // Deserialize each journal, build the ticket, and optionally filter
        Set<String> keys = jedis.keys(key(repository, KeyType.journal, "*"));
        for (String key : keys) {
            // {repo}:journal:{id}
            String id = key.split(":")[2];
            long ticketId = Long.parseLong(id);
            List<Change> changes = getJournal(jedis, repository, ticketId);
            if (ArrayUtils.isEmpty(changes)) {
                log.warn("Empty journal for {}:{}", repository, ticketId);
                continue;
            }
            TicketModel ticket = TicketModel.buildTicket(changes);
            ticket.project = repository.projectPath;
            ticket.repository = repository.name;
            ticket.number = ticketId;
            // add the ticket, conditionally, to the list
            if (filter == null) {
                list.add(ticket);
            } else {
                if (filter.accept(ticket)) {
                    list.add(ticket);
                }
            }
        }
        // sort the tickets by creation
        Collections.sort(list);
    } catch (JedisException e) {
        log.error("failed to retrieve tickets from Redis @ " + getUrl(), e);
        pool.returnBrokenResource(jedis);
        jedis = null;
    } finally {
        if (jedis != null) {
            pool.returnResource(jedis);
        }
    }
    return list;
}
Also used : Jedis(redis.clients.jedis.Jedis) JedisException(redis.clients.jedis.exceptions.JedisException) ArrayList(java.util.ArrayList) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change)

Aggregations

TicketModel (com.gitblit.models.TicketModel)62 Change (com.gitblit.models.TicketModel.Change)32 Test (org.junit.Test)28 RevCommit (org.eclipse.jgit.revwalk.RevCommit)21 IOException (java.io.IOException)18 Reference (com.gitblit.models.TicketModel.Reference)16 Repository (org.eclipse.jgit.lib.Repository)12 Patchset (com.gitblit.models.TicketModel.Patchset)8 RepositoryModel (com.gitblit.models.RepositoryModel)7 TicketLink (com.gitblit.models.TicketModel.TicketLink)7 Ref (org.eclipse.jgit.lib.Ref)5 UserModel (com.gitblit.models.UserModel)4 ArrayList (java.util.ArrayList)4 ReceiveCommand (org.eclipse.jgit.transport.ReceiveCommand)4 Attachment (com.gitblit.models.TicketModel.Attachment)3 ParseException (java.text.ParseException)3 RevWalk (org.eclipse.jgit.revwalk.RevWalk)3 TicketHook (com.gitblit.extensions.TicketHook)2 PathChangeModel (com.gitblit.models.PathModel.PathChangeModel)2 BranchTicketService (com.gitblit.tickets.BranchTicketService)2