Skip to content

Commit

Permalink
JENKINS-39192 - support for updating custom fields
Browse files Browse the repository at this point in the history
Merge branch 'tequillaz-feature/issue_custom_field_updater'

* tequillaz-feature/issue_custom_field_updater:
  Add JiraIssueFieldUpdater and JiraIssueField
  • Loading branch information
rantoniuk committed Oct 23, 2016
2 parents df10052 + 8c0dab8 commit 48c9bff
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 1 deletion.
68 changes: 68 additions & 0 deletions src/main/java/hudson/plugins/jira/JiraIssueField.java
@@ -0,0 +1,68 @@
package hudson.plugins.jira;

import java.lang.String;

public class JiraIssueField implements Comparable<JiraIssueField> {

private final String fieldId;
private final Object fieldValue;

public JiraIssueField(String fieldId, Object fieldValue) {
this.fieldId = fieldId;
this.fieldValue = fieldValue;
}

public int compareTo(JiraIssueField that) {
return this.compareTo(that);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fieldId == null) ? 0 : fieldId.hashCode());
result = prime * result + ((fieldValue == null) ? 0 : fieldValue.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}

JiraIssueField other = (JiraIssueField) obj;
if (fieldId == null) {
if (other.fieldId != null) {
return false;
}
} else if (!fieldId.equals(other.fieldId)) {
return false;
}

if (fieldValue == null) {
if (other.fieldValue != null) {
return false;
}
} else if (!fieldValue.equals(other.fieldValue)) {
return false;
}

return true;
}

public String getId() {
return fieldId;
}

public Object getValue() {
return fieldValue;
}

}
186 changes: 186 additions & 0 deletions src/main/java/hudson/plugins/jira/JiraIssueFieldUpdater.java
@@ -0,0 +1,186 @@
package hudson.plugins.jira;

import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletException;

import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import com.atlassian.jira.rest.client.api.RestClientException;
import com.google.common.collect.Lists;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.matrix.MatrixRun;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.jira.selector.AbstractIssueSelector;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import jenkins.tasks.SimpleBuildStep;

/**
* Issue custom fields updater
*
* @author Dmitry Frolov tekillaz.dev@gmail.com
*
*/
public class JiraIssueFieldUpdater extends Builder implements SimpleBuildStep {

private AbstractIssueSelector issueSelector;

public AbstractIssueSelector getIssueSelector() {
return this.issueSelector;
}

@DataBoundSetter
public void setIssueSelector(AbstractIssueSelector issueSelector) {
this.issueSelector = issueSelector;
}

public String fieldId;

public String getFieldId() {
return this.fieldId;
}

@DataBoundSetter
public void setFieldId(String fieldId) {
this.fieldId = fieldId;
}

public String fieldValue;

public String getFieldValue() {
return this.fieldValue;
}

@DataBoundSetter
public void setFieldValue(String fieldValue) {
this.fieldValue = fieldValue;
}

@DataBoundConstructor
public JiraIssueFieldUpdater(AbstractIssueSelector issueSelector, String fieldId, String fieldValue) {
this.issueSelector = issueSelector;
this.fieldId = fieldId;
this.fieldValue = fieldValue;
}

public String prepareFieldId(String fieldId) {
String prepared = fieldId;
if (!prepared.startsWith("customfield_"))
prepared = "customfield_" + prepared;
return prepared;
}

@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
PrintStream logger = listener.getLogger();

AbstractIssueSelector selector = issueSelector;
if (selector == null) {
logger.println("[JIRA][JiraIssueFieldUpdater] No issue selector found!");
throw new IOException("[JIRA][JiraIssueFieldUpdater] No issue selector found!");
}

JiraSite site = JiraSite.get(run.getParent());
if (site == null) {
logger.println(Messages.Updater_NoJiraSite());
run.setResult(Result.FAILURE);
return;
}

JiraSession session = null;
try {
session = site.getSession();
} catch (IOException e) {
listener.getLogger().println(Messages.Updater_FailedToConnect());
e.printStackTrace(listener.getLogger());
}
if (session == null) {
logger.println(Messages.Updater_NoRemoteAccess());
run.setResult(Result.FAILURE);
return;
}

Set<String> issues = selector.findIssueIds(run, site, listener);
if (issues.isEmpty()) {
logger.println("[JIRA][JiraIssueFieldUpdater] Issue list is empty!");
return;
}

List<JiraIssueField> fields = Lists.newArrayList();
fields.add(new JiraIssueField(prepareFieldId(fieldId), fieldValue));

for (String issue : issues) {
submitFields(session, issue, fields, logger);
}
}

public void submitFields(JiraSession session, String issueId, List<JiraIssueField> fields, PrintStream logger) {
try {
session.addFields(issueId, fields);
} catch (RestClientException e) {

if (e.getStatusCode().or(0).equals(404)) {
logger.println(issueId + " - JIRA issue not found");
}

if (e.getStatusCode().or(0).equals(403)) {
logger.println(issueId
+ " - Jenkins JIRA user does not have permissions to comment on this issue");
}

if (e.getStatusCode().or(0).equals(401)) {
logger.println(
issueId + " - Jenkins JIRA authentication problem");
}

logger.println(Messages.Updater_FailedToCommentOnIssue(issueId));
logger.println(e.getLocalizedMessage());
}
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

@Extension
public static class DescriptorImpl extends BuildStepDescriptor<Builder> {

public FormValidation doCheckField_id(@QueryParameter String value) throws IOException, ServletException {
if (Util.fixNull(value).trim().length() == 0)
return FormValidation.warning(Messages.JiraIssueFieldUpdater_NoIssueFieldID());
if (!value.matches("\\d+"))
return FormValidation.error(Messages.JiraIssueFieldUpdater_NotAtIssueFieldID());
return FormValidation.ok();
}

@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}

@Override
public String getDisplayName() {
return Messages.JiraIssueFieldUpdater_DisplayName();
}
}

}
13 changes: 13 additions & 0 deletions src/main/java/hudson/plugins/jira/JiraRestService.java
Expand Up @@ -274,6 +274,19 @@ public void setIssueLabels(String issueKey, List<String> labels) {
}
}

public void setIssueFields(String issueKey, List<JiraIssueField> fields) {
IssueInputBuilder builder = new IssueInputBuilder();
for (JiraIssueField field : fields)
builder.setFieldValue(field.getId(), field.getValue());
final IssueInput issueInput = builder.build();

try {
jiraRestClient.getIssueClient().updateIssue(issueKey, issueInput).get(timeout, TimeUnit.SECONDS);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "jira rest client update fields error for issue " + issueKey, e);
}
}

public Issue progressWorkflowAction(String issueKey, Integer actionId) {
final TransitionInput transitionInput = new TransitionInput(actionId);

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/hudson/plugins/jira/JiraSession.java
Expand Up @@ -94,6 +94,17 @@ public void addLabels(String issueId, List<String> labels) {
service.setIssueLabels(issueId, newLabels);
}
}

/**
* Adds new to or updates existing fields of the issue.
* Can add or update custom fields.
*
* @param issueId Jira issue ID like "PRJ-123"
* @param fields Fields to add or update
*/
public void addFields(String issueId, List<JiraIssueField> fields) {
service.setIssueFields(issueId, fields);
}

/**
* Gets the details of one issue.
Expand Down
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:dropdownDescriptorSelector field="issueSelector" title="${%Issue selector}"/>
<f:entry field="fieldId" title="Custom field ID">
<f:textbox />
</f:entry>
<f:entry field="fieldValue" title="Field value">
<f:textbox />
</f:entry>
</j:jelly>
@@ -0,0 +1,3 @@
<div>
Issue custom field ID. For example, 10100
</div>
@@ -0,0 +1,3 @@
<div>
JiraIssueFieldUpdater can be used to update custom JIRA issue field.
</div>
5 changes: 4 additions & 1 deletion src/main/resources/hudson/plugins/jira/Messages.properties
Expand Up @@ -37,4 +37,7 @@ IssueSelectorStep.Descriptor.DisplayName=JIRA: Issue selector
JiraCreateIssueNotifier.DisplayName=JIRA: Create issue
IssueSelector.ExplicitIssueSelector.DisplayName=Explicit selector
IssueSelector.JqlIssueSelector.DisplayName=JQL selector
JiraVersionCreatorBuilder.DisplayName=JIRA: Create new version
JiraVersionCreatorBuilder.DisplayName=JIRA: Create new version
JiraIssueFieldUpdater.DisplayName=JIRA: Issue custom field updater
JiraIssueFieldUpdater.NoIssueFieldID=Issue field id required
JiraIssueFieldUpdater.NotAtIssueFieldID=Not an issue field id

0 comments on commit 48c9bff

Please sign in to comment.