Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #3 from aquarellian/master
JENKINS-31004 - Check gerrit-trigger REST API settings before plugin …
  • Loading branch information
aquarellian committed Oct 22, 2015
2 parents 2994332 + d9be156 commit a81eadc
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 105 deletions.
Expand Up @@ -13,7 +13,6 @@
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.sonyericsson.hudson.plugins.gerrit.trigger.GerritManagement;
import com.sonyericsson.hudson.plugins.gerrit.trigger.VerdictCategory;
import com.sonyericsson.hudson.plugins.gerrit.trigger.config.IGerritHudsonTriggerConfig;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger;
import com.urswolfer.gerrit.client.rest.GerritAuthData;
Expand Down Expand Up @@ -63,6 +62,8 @@ public class SonarToGerritBuilder extends Builder {
private static final String DEFAULT_SONAR_URL = "http://localhost:9000";
private static final String DEFAULT_CATEGORY = "Code-Review";
private static final int DEFAULT_SCORE = 0;
private static final ReviewInput.NotifyHandling DEFAULT_NOTIFICATION_NO_ISSUES = ReviewInput.NotifyHandling.NONE;
private static final ReviewInput.NotifyHandling DEFAULT_NOTIFICATION_ISSUES = ReviewInput.NotifyHandling.OWNER;

public static final String GERRIT_FILE_DELIMITER = "/";
public static final String EMPTY_STR = "";
Expand All @@ -86,12 +87,16 @@ public class SonarToGerritBuilder extends Builder {
private final String noIssuesScore;
private final String issuesScore;

private final String noIssuesNotification;
private final String issuesNotification;


@DataBoundConstructor
public SonarToGerritBuilder(String projectPath, String sonarURL, String path,
String severity, boolean changedLinesOnly, boolean newIssuesOnly,
String noIssuesToPostText, String someIssuesToPostText, String issueComment,
boolean postScore, String category, String noIssuesScore, String issuesScore) {
boolean postScore, String category, String noIssuesScore, String issuesScore,
String noIssuesNotification, String issuesNotification) {
this.projectPath = MoreObjects.firstNonNull(projectPath, EMPTY_STR);
this.sonarURL = MoreObjects.firstNonNull(sonarURL, DEFAULT_SONAR_URL);
this.path = MoreObjects.firstNonNull(path, DEFAULT_PATH);
Expand All @@ -105,6 +110,8 @@ public SonarToGerritBuilder(String projectPath, String sonarURL, String path,
this.category = MoreObjects.firstNonNull(category, DEFAULT_CATEGORY);
this.noIssuesScore = noIssuesScore;
this.issuesScore = issuesScore;
this.noIssuesNotification = noIssuesNotification;
this.issuesNotification = issuesNotification;
}

public String getPath() {
Expand Down Expand Up @@ -143,6 +150,7 @@ public String getProjectPath() {
return projectPath;
}

@SuppressWarnings(value = "unused")
public boolean isPostScore() {
return postScore;
}
Expand All @@ -151,10 +159,22 @@ public String getCategory() {
return category;
}

@SuppressWarnings(value = "unused")
public String getNoIssuesScore() {
return noIssuesScore;
}

@SuppressWarnings(value = "unused")
public String getNoIssuesNotification() {
return noIssuesNotification;
}

@SuppressWarnings(value = "unused")
public String getIssuesNotification() {
return issuesNotification;
}

@SuppressWarnings(value = "unused")
public String getIssuesScore() {
return issuesScore;
}
Expand Down Expand Up @@ -196,11 +216,16 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis
logError(listener, "jenkins.plugin.error.gerrit.config.empty", Level.SEVERE);
return false;
}
GerritRestApiFactory gerritRestApiFactory = new GerritRestApiFactory();

if (!gerritConfig.isUseRestApi()) {
logError(listener, "jenkins.plugin.error.gerrit.restapi.off", Level.SEVERE);
return false;
}
if (gerritConfig.getGerritHttpUserName() == null) {
logError(listener, "jenkins.plugin.error.gerrit.user.empty", Level.SEVERE);
return false;
}
GerritRestApiFactory gerritRestApiFactory = new GerritRestApiFactory();
GerritAuthData.Basic authData = new GerritAuthData.Basic(gerritConfig.getGerritFrontEndUrl(),
gerritConfig.getGerritHttpUserName(), gerritConfig.getGerritHttpPassword());
GerritApi gerritApi = gerritRestApiFactory.create(authData);
Expand Down Expand Up @@ -228,15 +253,15 @@ public boolean apply(@Nullable String input) {
}

// Step 6 - Send review to Gerrit
Collection<String> categoryNames = getCategoryNames(gerritConfig.getCategories());
ReviewInput reviewInput = getReviewResult(file2issues, categoryNames);
ReviewInput reviewInput = getReviewResult(file2issues);

// Step 7 - Post review
revision.review(reviewInput);
LOGGER.log(Level.INFO, "Review has been sent");
} catch (RestApiException e) {
LOGGER.severe(e.getMessage());
return true;
listener.getLogger().println("Unable to post review: " + e.getMessage());
LOGGER.severe("Unable to post review: " + e.getMessage());
return false;
}

return true;
Expand All @@ -257,11 +282,21 @@ private String getReviewMessage(Multimap<String, Issue> finalIssues) {
return new CustomReportFormatter(finalIssues.values(), someIssuesToPostText, noIssuesToPostText).getMessage();
}

private int getReviewMark(Multimap<String, Issue> finalIssues) {
String mark = finalIssues.size() > 0 ? issuesScore : noIssuesScore;
private int getReviewMark(int finalIssuesCount) {
String mark = finalIssuesCount > 0 ? issuesScore : noIssuesScore;
return parseNumber(mark, DEFAULT_SCORE);
}

private ReviewInput.NotifyHandling getNotificationSettings(int finalIssuesCount) {
if (finalIssuesCount > 0) {
ReviewInput.NotifyHandling value = (issuesNotification == null ? null : ReviewInput.NotifyHandling.valueOf(issuesNotification));
return MoreObjects.firstNonNull(value, DEFAULT_NOTIFICATION_ISSUES);
} else {
ReviewInput.NotifyHandling value = (noIssuesNotification == null ? null : ReviewInput.NotifyHandling.valueOf(noIssuesNotification));
return MoreObjects.firstNonNull(value, DEFAULT_NOTIFICATION_NO_ISSUES);
}
}

private int parseNumber(String number, int deflt) {
try {
return Integer.parseInt(number);
Expand All @@ -272,10 +307,18 @@ private int parseNumber(String number, int deflt) {
}

@VisibleForTesting
ReviewInput getReviewResult(Multimap<String, Issue> finalIssues, Collection<String> existingCategories) {
ReviewInput getReviewResult(Multimap<String, Issue> finalIssues) {
String reviewMessage = getReviewMessage(finalIssues);
ReviewInput reviewInput = new ReviewInput().message(reviewMessage);

int finalIssuesCount = finalIssues.size();

reviewInput.notify = getNotificationSettings(finalIssuesCount);

if (postScore) {
reviewInput.label(category, getReviewMark(finalIssuesCount));
}

reviewInput.comments = new HashMap<String, List<ReviewInput.CommentInput>>();
for (String file : finalIssues.keySet()) {
reviewInput.comments.put(file, Lists.newArrayList(
Expand All @@ -299,26 +342,9 @@ public ReviewInput.CommentInput apply(@Nullable Issue input) {
)
);
}
if (postScore) {
String realCategory = getRealCategory(existingCategories);
reviewInput.label(realCategory, getReviewMark(finalIssues));
}
return reviewInput;
}

private Collection<String> getCategoryNames(List<VerdictCategory> categories){
Set<String> availableCategories = new HashSet<String>();
for (VerdictCategory verdictCategory : categories) {
availableCategories.add(verdictCategory.getVerdictDescription());
}
return availableCategories;
}

protected String getRealCategory(Collection<String> categories) {
// todo notify user about switching category to default?
return categories.contains(category) ? category : DEFAULT_CATEGORY;
}

@VisibleForTesting
void filterIssuesByChangedLines(Multimap<String, Issue> finalIssues, RevisionApi revision) throws RestApiException {
for (String filename : new HashSet<String>(finalIssues.keySet())) {
Expand Down Expand Up @@ -581,18 +607,54 @@ public FormValidation doCheckIssuesScore(@QueryParameter String value) {
}

private FormValidation checkScore(@QueryParameter String value) {
try{
try {
Integer.parseInt(value);
} catch (NumberFormatException e){
} catch (NumberFormatException e) {
return FormValidation.error(getLocalized("jenkins.plugin.validation.review.score.not.numeric"));
}
return FormValidation.ok();
}

/**
* Performs on-the-fly validation of the form field 'noIssuesNotification'.
*
* @param value This parameter receives the value that the user has typed.
* @return Indicates the outcome of the validation. This is sent to the browser.
* <p/>
* Note that returning {@link FormValidation#error(String)} does not
* prevent the form from being saved. It just means that a message
* will be displayed to the user.
*/
@SuppressWarnings(value = "unused")
public FormValidation doCheckNoIssuesNotification(@QueryParameter String value) {
return checkNotificationType(value);
}

/**
* Performs on-the-fly validation of the form field 'issuesNotification'.
*
* @param value This parameter receives the value that the user has typed.
* @return Indicates the outcome of the validation. This is sent to the browser.
* <p/>
* Note that returning {@link FormValidation#error(String)} does not
* prevent the form from being saved. It just means that a message
* will be displayed to the user.
*/
@SuppressWarnings(value = "unused")
public FormValidation doCheckIssuesNotification(@QueryParameter String value) {
return checkNotificationType(value);
}

private FormValidation checkNotificationType(@QueryParameter String value) {
if (value == null || ReviewInput.NotifyHandling.valueOf(value) == null) {
return FormValidation.error(getLocalized("jenkins.plugin.validation.review.notification.recipient.unknown"));
}
return FormValidation.ok();
}

@Override
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
// Indicates that this builder can be used with all kinds of project types
//todo check if gerrit trigger installed
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/resources/messages.properties
Expand Up @@ -17,4 +17,6 @@ jenkins.plugin.error.gerrit.user.empty=Gerrit authentication is not configured.
jenkins.plugin.error.sonar.report.not.exists=Sonar report '%s' does not exist. Please check plugin settings
jenkins.plugin.validation.review.severity.unknown=Unknown severity
jenkins.plugin.validation.review.category.unknown=Unknown category
jenkins.plugin.validation.review.score.not.numeric=Score should be numeric
jenkins.plugin.validation.review.score.not.numeric=Score should be numeric
jenkins.plugin.error.gerrit.restapi.off=RestAPI in Gerrit-Trigger settings must be enabled
jenkins.plugin.validation.review.notification.recipient.unknown=Notification recipient is unknown
Expand Up @@ -10,12 +10,6 @@
Creates a text field that shows the value of the "path" property.
When submitted, it will be passed to the corresponding constructor parameter.
-->
<f:section title="${%jenkins.plugin.settings.section.project.name}">
<f:entry title="${%jenkins.plugin.settings.project.base.path}" field="projectPath">
<f:textbox/>
</f:entry>
</f:section>

<f:section title="${%jenkins.plugin.settings.section.sonar.name}">
<f:entry title="${%jenkins.plugin.settings.sonar.url}" field="sonarURL">
<f:textbox default="http://localhost:9000"/>
Expand All @@ -25,32 +19,16 @@
</f:entry>
</f:section>

<f:section title="${%jenkins.plugin.settings.section.gerrit.name}">
<f:entry title="${%jenkins.plugin.settings.gerrit.post.score}" field="postScore"
description="${%jenkins.plugin.settings.gerrit.post.score.description}">
<f:checkbox default="true"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.gerrit.category}" field="category">
<f:textbox default="Code-Review"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.gerrit.score.no.issues}" field="noIssuesScore">
<f:textbox default="+1" />
</f:entry>
<f:entry title="${%jenkins.plugin.settings.gerrit.score.issues}" field="issuesScore">
<f:textbox default="-1" />
</f:entry>
</f:section>

<f:section title="${%jenkins.plugin.settings.section.filter.name}">
<f:entry name="severity" title="${%jenkins.plugin.settings.filter.severity}" field="severity">
<select name="severity" field="severity">
<f:option value="INFO" selected="${instance.severity =='INFO'}">INFO</f:option>
<f:option value="MINOR" selected="${instance.severity =='MINOR'}">MINOR</f:option>
<f:option value="INFO" selected="${instance.severity =='INFO'}">${%INFO}</f:option>
<f:option value="MINOR" selected="${instance.severity =='MINOR'}">${%MINOR}</f:option>
<f:option value="MAJOR"
selected="${instance.severity =='MAJOR' || instance.severity == null}">MAJOR
selected="${instance.severity =='MAJOR' || instance.severity == null}">${%MAJOR}
</f:option>
<f:option value="CRITICAL" selected="${instance.severity =='CRITICAL'}">CRITICAL</f:option>
<f:option value="BLOCKER" selected="${instance.severity =='BLOCKER'}">BLOCKER</f:option>
<f:option value="CRITICAL" selected="${instance.severity =='CRITICAL'}">${%CRITICAL}</f:option>
<f:option value="BLOCKER" selected="${instance.severity =='BLOCKER'}">${%BLOCKER}</f:option>
</select>
</f:entry>

Expand All @@ -64,15 +42,80 @@
<f:checkbox default="true"/>
</f:entry>
</f:section>
<f:section title="${%jenkins.plugin.settings.section.review.format.name}">
<f:entry title="${%jenkins.plugin.settings.review.format.title.no.issues}" field="noIssuesToPostText">
<f:textbox default="${%jenkins.plugin.default.review.title.no.issues}"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.review.format.title.issues}" field="someIssuesToPostText">
<f:textbox default="${%jenkins.plugin.default.review.title.issues}"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.review.format.body}" field="issueComment">
<f:textarea default="${%jenkins.plugin.default.review.body}"/>
</f:entry>
</f:section>
<f:advanced>
<f:section title="${%jenkins.plugin.settings.section.project.name}">
<f:entry title="${%jenkins.plugin.settings.project.base.path}" field="projectPath">
<f:textbox/>
</f:entry>
</f:section>

<f:section title="${%jenkins.plugin.settings.section.review.format.name}">
<f:entry title="${%jenkins.plugin.settings.review.format.title.no.issues}" field="noIssuesToPostText">
<f:textbox default="${%jenkins.plugin.default.review.title.no.issues}"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.review.format.title.issues}" field="someIssuesToPostText">
<f:textbox default="${%jenkins.plugin.default.review.title.issues}"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.review.format.body}" field="issueComment">
<f:textarea default="${%jenkins.plugin.default.review.body}"/>
</f:entry>
</f:section>

<f:section title="${%jenkins.plugin.settings.section.gerrit.name}">
<f:entry title="${%jenkins.plugin.settings.gerrit.post.score}" field="postScore"
description="${%jenkins.plugin.settings.gerrit.post.score.description}">
<f:checkbox default="true"/>
</f:entry>

<!--<f:entry title="${%jenkins.plugin.settings.gerrit.post.score}" field="postScore" help="help-postScore">-->
<!--&lt;!&ndash;description="${%jenkins.plugin.settings.gerrit.post.score.description}">&ndash;&gt;-->
<!--&lt;!&ndash;<f:checkbox default="true"/>&ndash;&gt;-->
<!--<table width="100%">-->

<!--<f:optionalBlock title="" name="postScore"-->
<!--checked="${it.postScore}" default="true">-->
<f:entry title="${%jenkins.plugin.settings.gerrit.category}" field="category">
<f:textbox default="Code-Review"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.gerrit.score.no.issues}" field="noIssuesScore">
<f:textbox default="+1"/>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.gerrit.score.issues}" field="issuesScore">
<f:textbox default="-1"/>
</f:entry>
<!--</f:optionalBlock>-->
<!--</table>-->
<!--</f:entry>-->
</f:section>

<f:section title="${%jenkins.plugin.settings.section.notification.settings.name}">
<f:entry title="${%jenkins.plugin.settings.notification.no.issues}" field="noIssuesNotification">
<select name="noIssuesNotification" field="severity">
<f:option value="NONE"
selected="${instance.noIssuesNotification =='NONE' || instance.noIssuesNotification == null}">
${%NONE}
</f:option>
<f:option value="OWNER" selected="${instance.noIssuesNotification =='OWNER'}">${%OWNER}</f:option>
<f:option value="OWNER_REVIEWERS"
selected="${instance.noIssuesNotification =='OWNER_REVIEWERS'}">${%OWNER_REVIEWERS}
</f:option>
<f:option value="ALL" selected="${instance.noIssuesNotification =='ALL'}">${%ALL}</f:option>

</select>
</f:entry>
<f:entry title="${%jenkins.plugin.settings.notification.issues}" field="issuesNotification">
<select name="issuesNotification" field="severity">
<f:option value="NONE" selected="${instance.issuesNotification =='NONE'}">${%NONE}</f:option>
<f:option value="OWNER"
selected="${instance.issuesNotification =='OWNER' || instance.issuesNotification == null}">
${%OWNER}
</f:option>
<f:option value="OWNER_REVIEWERS"
selected="${instance.issuesNotification =='OWNER_REVIEWERS'}">${%OWNER_REVIEWERS}
</f:option>
<f:option value="ALL" selected="${instance.issuesNotification =='ALL'}">${%ALL}</f:option>
</select>
</f:entry>
</f:section>
</f:advanced>
</j:jelly>

0 comments on commit a81eadc

Please sign in to comment.