org.sonar.plugins.github.PullRequestIssuePostJob.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.github.PullRequestIssuePostJob.java

Source

/*
 * SonarQube :: GitHub MultiModule Plugin
 * Copyright (C) 2015-2017 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.plugins.github;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.github.GHCommitState;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.postjob.PostJob;
import org.sonar.api.batch.postjob.PostJobContext;
import org.sonar.api.batch.postjob.PostJobDescriptor;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.StreamSupport;

/**
 * Compute comments to be added on the pull request.
 */
public class PullRequestIssuePostJob implements PostJob {
    private static final Logger LOG = Loggers.get(PullRequestFacade.class);

    private static final Comparator<PostJobIssue> ISSUE_COMPARATOR = new IssueComparator();

    private final PullRequestFacade pullRequestFacade;
    private final GitHubPluginConfiguration gitHubPluginConfiguration;
    private final MarkDownUtils markDownUtils;

    public PullRequestIssuePostJob(GitHubPluginConfiguration gitHubPluginConfiguration,
            PullRequestFacade pullRequestFacade, MarkDownUtils markDownUtils) {
        this.gitHubPluginConfiguration = gitHubPluginConfiguration;
        this.pullRequestFacade = pullRequestFacade;
        this.markDownUtils = markDownUtils;
    }

    @Override
    public void describe(PostJobDescriptor descriptor) {
        descriptor.name("GitHub Pull Request Issue Publisher")
                .requireProperty(GitHubMultiModule.GITHUB_PULL_REQUEST);
    }

    @Override
    public void execute(PostJobContext context) {
        GlobalReport report = new GlobalReport(gitHubPluginConfiguration.module(), markDownUtils,
                gitHubPluginConfiguration.tryReportIssuesInline());
        try {
            Map<InputFile, Map<Integer, StringBuilder>> commentsToBeAddedByLine = processIssues(report,
                    context.issues());

            updateReviewComments(commentsToBeAddedByLine);

            pullRequestFacade.deleteOutdatedComments();

            List<Integer> issuesCriticalAndBlockers = pullRequestFacade.getRelevantOldIssues();

            pullRequestFacade
                    .createOrUpdateGlobalComments(report.hasNewIssue() ? report.formatForMarkdown() : null);

            pullRequestFacade.createOrUpdateSonarQubeStatus(report.getStatus(issuesCriticalAndBlockers),
                    report.getStatusDescription(issuesCriticalAndBlockers));
        } catch (Exception e) {
            LOG.error("SonarQube analysis failed to complete the review of this pull request", e);
            pullRequestFacade.createOrUpdateSonarQubeStatus(GHCommitState.ERROR,
                    StringUtils.abbreviate("SonarQube analysis failed: " + e.getMessage(), 140));
        }
    }

    private Map<InputFile, Map<Integer, StringBuilder>> processIssues(GlobalReport report,
            Iterable<PostJobIssue> issues) {
        Map<InputFile, Map<Integer, StringBuilder>> commentToBeAddedByFileAndByLine = new HashMap<>();

        StreamSupport.stream(issues.spliterator(), false).filter(PostJobIssue::isNew)
                // SONARGITUB-13 Ignore issues on files not modified by the P/R
                .filter(i -> {
                    InputComponent inputComponent = i.inputComponent();
                    return inputComponent == null || !inputComponent.isFile()
                            || pullRequestFacade.hasFile((InputFile) inputComponent);
                }).sorted(ISSUE_COMPARATOR).forEach(i -> processIssue(report, commentToBeAddedByFileAndByLine, i));
        return commentToBeAddedByFileAndByLine;

    }

    private void processIssue(GlobalReport report,
            Map<InputFile, Map<Integer, StringBuilder>> commentToBeAddedByFileAndByLine, PostJobIssue issue) {
        boolean reportedInline = false;
        InputComponent inputComponent = issue.inputComponent();
        if (gitHubPluginConfiguration.tryReportIssuesInline() && inputComponent != null
                && inputComponent.isFile()) {
            reportedInline = tryReportInline(commentToBeAddedByFileAndByLine, issue, (InputFile) inputComponent);
        }
        report.process(issue, pullRequestFacade.getGithubUrl(inputComponent, issue.line()), reportedInline);
    }

    private boolean tryReportInline(Map<InputFile, Map<Integer, StringBuilder>> commentToBeAddedByFileAndByLine,
            PostJobIssue issue, InputFile inputFile) {
        Integer lineOrNull = issue.line();
        if (lineOrNull != null) {
            int line = lineOrNull.intValue();
            if (pullRequestFacade.hasFileLine(inputFile, line)) {
                String message = issue.message();
                String ruleKey = issue.ruleKey().toString();
                if (!commentToBeAddedByFileAndByLine.containsKey(inputFile)) {
                    commentToBeAddedByFileAndByLine.put(inputFile, new HashMap<Integer, StringBuilder>());
                }
                Map<Integer, StringBuilder> commentsByLine = commentToBeAddedByFileAndByLine.get(inputFile);
                if (!commentsByLine.containsKey(line)) {
                    commentsByLine.put(line, new StringBuilder());
                }
                commentsByLine.get(line).append(markDownUtils.inlineIssue(issue.severity(), message, ruleKey))
                        .append("\n");
                return true;
            }
        }
        return false;
    }

    private void updateReviewComments(Map<InputFile, Map<Integer, StringBuilder>> commentsToBeAddedByLine) {
        for (Map.Entry<InputFile, Map<Integer, StringBuilder>> entry : commentsToBeAddedByLine.entrySet()) {
            for (Map.Entry<Integer, StringBuilder> entryPerLine : entry.getValue().entrySet()) {
                String body = entryPerLine.getValue().toString();
                pullRequestFacade.createOrUpdateReviewComment(entry.getKey(), entryPerLine.getKey(), body);
            }
        }
    }

}