-
-
Notifications
You must be signed in to change notification settings - Fork 282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Jira Issue update step to pipeline #101
base: master
Are you sure you want to change the base?
Changes from all commits
3a7212b
69621d8
9f30e52
0f9583c
da4fac3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,10 +18,7 @@ | |
import com.atlassian.jira.rest.client.api.JiraRestClient; | ||
import com.atlassian.jira.rest.client.api.RestClientException; | ||
import com.atlassian.jira.rest.client.api.domain.*; | ||
import com.atlassian.jira.rest.client.api.domain.input.IssueInput; | ||
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder; | ||
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput; | ||
import com.atlassian.jira.rest.client.api.domain.input.VersionInput; | ||
import com.atlassian.jira.rest.client.api.domain.input.*; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.google.common.collect.Iterators; | ||
|
@@ -368,4 +365,19 @@ public String getBaseApiPath() { | |
public Permissions getMyPermissions() throws RestClientException { | ||
return jiraRestClient.getMyPermissionsRestClient().getMyPermissions(null).claim(); | ||
} | ||
|
||
public boolean updateIssueFieldValue(String issueKey, String fieldName, String fieldValue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think it should be called |
||
IssueField field = getIssue(issueKey).getFieldByName(fieldName); | ||
FieldInput fieldInput = new FieldInput(field.getId(), fieldValue); | ||
|
||
IssueInput issueInput = IssueInput.createWithFields(fieldInput); | ||
|
||
try { | ||
jiraRestClient.getIssueClient().updateIssue(issueKey, issueInput).get(timeout, TimeUnit.SECONDS); | ||
return true; | ||
} catch (Exception e) { | ||
LOGGER.warning("jira rest client update issue field error. cause: " + e.getMessage()); | ||
} | ||
return false; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add new line |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package hudson.plugins.jira.pipeline; | ||
|
||
import com.google.inject.Inject; | ||
import hudson.Extension; | ||
import hudson.Util; | ||
import hudson.model.Result; | ||
import hudson.model.Run; | ||
import hudson.model.TaskListener; | ||
import hudson.plugins.jira.JiraSite; | ||
import hudson.plugins.jira.Messages; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution; | ||
import org.jenkinsci.plugins.workflow.steps.StepContextParameter; | ||
import org.kohsuke.stapler.DataBoundConstructor; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.io.IOException; | ||
|
||
/** | ||
* Step for updating jira issue with workflow migration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this one is doing something different... |
||
* | ||
* @author aatarasoff | ||
*/ | ||
public class IssueFieldUpdateStep extends AbstractStepImpl { | ||
private final String issueKey; | ||
private final String fieldName; | ||
private final String fieldValue; | ||
|
||
@DataBoundConstructor | ||
public IssueFieldUpdateStep(@Nonnull String issueKey, @Nonnull String fieldName, @Nonnull String fieldValue) { | ||
this.issueKey = Util.fixEmptyAndTrim(issueKey); | ||
this.fieldName = Util.fixEmptyAndTrim(fieldName); | ||
this.fieldValue = Util.fixEmptyAndTrim(fieldValue); | ||
} | ||
|
||
public String getIssueKey() { | ||
return issueKey; | ||
} | ||
|
||
public String getFieldName() { | ||
return fieldName; | ||
} | ||
|
||
public String getFieldValue() { | ||
return fieldValue; | ||
} | ||
|
||
@Extension(optional = true) | ||
public static final class DescriptorImpl extends AbstractStepDescriptorImpl { | ||
|
||
public DescriptorImpl() { | ||
super(IssueFieldUpdateStep.StepExecution.class); | ||
} | ||
|
||
@Override | ||
public String getFunctionName() { | ||
return "jiraIssueFieldUpdate"; | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return Messages.IssueFieldUpdateStep_Descriptor_DisplayName(); | ||
} | ||
} | ||
|
||
public static class StepExecution extends AbstractSynchronousNonBlockingStepExecution<Void> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
@Inject | ||
private transient IssueFieldUpdateStep step; | ||
|
||
@StepContextParameter | ||
private transient TaskListener listener; | ||
|
||
@StepContextParameter | ||
private transient Run run; | ||
|
||
@Override | ||
protected Void run() throws Exception { | ||
JiraSite site = JiraSite.get(run.getParent()); | ||
|
||
if (site == null) { | ||
listener.getLogger().println(Messages.Updater_NoJiraSite()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
listener.getLogger().println("[JIRA] Update issue with key: " + step.getIssueKey()); | ||
|
||
try { | ||
if (!site.getSession().updateIssueFieldValue(step.getIssueKey(), step.getFieldName(), step.getFieldValue())) { | ||
listener.getLogger().println(Messages.IssueFieldUpdateStep_Failed()); | ||
run.setResult(Result.UNSTABLE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why UNSTABLE here and FAILURE in other cases? I think we should stick to one failure type in this case. |
||
} | ||
} catch (IOException e) { | ||
listener.getLogger().println(Messages.IssueFieldUpdateStep_Failed()); | ||
e.printStackTrace(listener.getLogger()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package hudson.plugins.jira.pipeline; | ||
|
||
import com.google.inject.Inject; | ||
import hudson.Extension; | ||
import hudson.Util; | ||
import hudson.model.Result; | ||
import hudson.model.Run; | ||
import hudson.model.TaskListener; | ||
import hudson.plugins.jira.JiraSite; | ||
import hudson.plugins.jira.Messages; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution; | ||
import org.jenkinsci.plugins.workflow.steps.StepContextParameter; | ||
import org.kohsuke.stapler.DataBoundConstructor; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.io.IOException; | ||
|
||
/** | ||
* Step for updating jira issue with workflow migration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest to re-word, as I don't understand it - maybe let's stick to conform to JIRA terminology about workflow transition :-) |
||
* | ||
* @author aatarasoff | ||
*/ | ||
public class IssueUpdateStep extends AbstractStepImpl { | ||
private final String jqlSearch; | ||
private final String workflowActionName; | ||
private final String comment; | ||
|
||
@DataBoundConstructor | ||
public IssueUpdateStep(@Nonnull String jqlSearch, @Nonnull String workflowActionName, String comment) { | ||
this.jqlSearch = Util.fixEmptyAndTrim(jqlSearch); | ||
this.workflowActionName = Util.fixEmptyAndTrim(workflowActionName); | ||
this.comment = Util.fixEmptyAndTrim(comment); | ||
} | ||
|
||
public String getJqlSearch() { | ||
return jqlSearch; | ||
} | ||
|
||
public String getWorkflowActionName() { | ||
return workflowActionName; | ||
} | ||
|
||
public String getComment() { | ||
return comment; | ||
} | ||
|
||
@Extension(optional = true) | ||
public static final class DescriptorImpl extends AbstractStepDescriptorImpl { | ||
|
||
public DescriptorImpl() { | ||
super(IssueUpdateStep.StepExecution.class); | ||
} | ||
|
||
@Override | ||
public String getFunctionName() { | ||
return "jiraIssueUpdate"; | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return Messages.IssueUpdateStep_Descriptor_DisplayName(); | ||
} | ||
} | ||
|
||
public static class StepExecution extends AbstractSynchronousNonBlockingStepExecution<Void> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
@Inject | ||
private transient IssueUpdateStep step; | ||
|
||
@StepContextParameter | ||
private transient TaskListener listener; | ||
|
||
@StepContextParameter | ||
private transient Run run; | ||
|
||
@Override | ||
protected Void run() throws Exception { | ||
JiraSite site = JiraSite.get(run.getParent()); | ||
|
||
if (site == null) { | ||
listener.getLogger().println(Messages.Updater_NoJiraSite()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
if (StringUtils.isNotEmpty(step.getWorkflowActionName())) { | ||
listener.getLogger().println(Messages.JiraIssueUpdateBuilder_UpdatingWithAction(step.getWorkflowActionName())); | ||
} | ||
|
||
listener.getLogger().println("[JIRA] JQL: " + step.getJqlSearch()); | ||
|
||
try { | ||
if (!site.progressMatchingIssues(step.getJqlSearch(), step.workflowActionName, step.getComment(), listener.getLogger())) { | ||
listener.getLogger().println(Messages.JiraIssueUpdateBuilder_SomeIssuesFailed()); | ||
run.setResult(Result.UNSTABLE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
} | ||
} catch (IOException e) { | ||
listener.getLogger().println(Messages.JiraIssueUpdateBuilder_Failed()); | ||
e.printStackTrace(listener.getLogger()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package hudson.plugins.jira.pipeline; | ||
|
||
import com.google.inject.Inject; | ||
import hudson.Extension; | ||
import hudson.Util; | ||
import hudson.model.Result; | ||
import hudson.model.Run; | ||
import hudson.model.TaskListener; | ||
import hudson.plugins.jira.JiraSite; | ||
import hudson.plugins.jira.Messages; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; | ||
import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution; | ||
import org.jenkinsci.plugins.workflow.steps.StepContextParameter; | ||
import org.kohsuke.stapler.DataBoundConstructor; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
/** | ||
* Created by aleksandr on 01.07.16. | ||
*/ | ||
public class IssueWorkflowActionStep extends AbstractStepImpl { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add class description |
||
private final String issueKey; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no variable expansion here, I'm wondering what usecase is this going to be, because you'll need to configure "issueKey" as static value in the plugin configuration. Maybe you could add variable expansion (see JiraIssueUpdateBuilder for example) - that would give some additional great power to those functionalities, what do you think? |
||
private final String workflowActionName; | ||
|
||
@DataBoundConstructor | ||
public IssueWorkflowActionStep(@Nonnull String issueKey, @Nonnull String workflowActionName) { | ||
this.issueKey = Util.fixEmptyAndTrim(issueKey); | ||
this.workflowActionName = Util.fixEmptyAndTrim(workflowActionName); | ||
} | ||
|
||
public String getIssueKey() { | ||
return issueKey; | ||
} | ||
|
||
public String getWorkflowActionName() { | ||
return workflowActionName; | ||
} | ||
|
||
@Extension(optional = true) | ||
public static final class DescriptorImpl extends AbstractStepDescriptorImpl { | ||
|
||
public DescriptorImpl() { | ||
super(IssueWorkflowActionStep.StepExecution.class); | ||
} | ||
|
||
@Override | ||
public String getFunctionName() { | ||
return "jiraIssueWorkflowStep"; | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return Messages.IssueWorkflowActionStep_Descriptor_DisplayName(); | ||
} | ||
} | ||
|
||
public static class StepExecution extends AbstractSynchronousNonBlockingStepExecution<Void> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
@Inject | ||
private transient IssueWorkflowActionStep step; | ||
|
||
@StepContextParameter | ||
private transient TaskListener listener; | ||
|
||
@StepContextParameter | ||
private transient Run run; | ||
|
||
@Override | ||
protected Void run() throws Exception { | ||
JiraSite site = JiraSite.get(run.getParent()); | ||
|
||
if (site == null) { | ||
listener.getLogger().println(Messages.Updater_NoJiraSite()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
listener.getLogger().println("[JIRA] Migrate issue with key: " + step.getIssueKey() + | ||
" to step: " + step.getWorkflowActionName() | ||
); | ||
|
||
try { | ||
long originalStatusId = site.getSession() | ||
.getIssue(step.getIssueKey()) | ||
.getStatus() | ||
.getId(); | ||
|
||
Integer actionId = site.getSession().getActionIdForIssue(step.getIssueKey(), step.getWorkflowActionName()); | ||
if (actionId == null) { | ||
throw new RuntimeException("Workflow action does not exists"); | ||
} | ||
|
||
site.getSession().progressWorkflowAction(step.getIssueKey(), actionId); | ||
|
||
long currentStatusId = site.getSession() | ||
.getIssue(step.getIssueKey()) | ||
.getStatus() | ||
.getId(); | ||
|
||
if (originalStatusId == currentStatusId){ | ||
listener.getLogger().println(Messages.IssueWorkflowActionStep_Failed()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
} catch (Exception e) { | ||
listener.getLogger().println(Messages.IssueWorkflowActionStep_Failed()); | ||
e.printStackTrace(listener.getLogger()); | ||
run.setResult(Result.FAILURE); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,4 +36,9 @@ SearchIssuesStep.Descriptor.DisplayName=JIRA: Search issues | |
IssueSelectorStep.Descriptor.DisplayName=JIRA: Issue selector | ||
JiraCreateIssueNotifier.DisplayName=JIRA: Create issue | ||
IssueSelector.ExplicitIssueSelector.DisplayName=Explicit selector | ||
IssueSelector.JqlIssueSelector.DisplayName=JQL selector | ||
IssueSelector.JqlIssueSelector.DisplayName=JQL selector | ||
IssueUpdateStep.Descriptor.DisplayName=JIRA: Progress issues by workflow action | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'Move issues through workflow' ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually progress is a good word, hence i suggest "JIRA: Progress issues through workflow" |
||
IssueFieldUpdateStep.Descriptor.DisplayName=JIRA: Update issue field | ||
IssueFieldUpdateStep.Failed=JIRA: Update issue field is failed | ||
IssueWorkflowActionStep.Descriptor.DisplayName=JIRA: Progress single issue by workflow action | ||
IssueWorkflowActionStep.Failed=JIRA: Progress issue is failed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please replace wildcard imports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming this is an automatic IDE feature (in IDEA) replacement, that when > 5 imports, wildcards are automatically incurred. Personally this is still debatable for me:
http://stackoverflow.com/questions/147454/why-is-using-a-wild-card-with-a-java-import-statement-bad
Any good reason in Jenkins case about not using wildcard imports that we should maybe put in the README guidelines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i just looked through few random classes in jenkins core repo and can't see wildcard imports used. it would be better to add checkstyle plugin to build rather than put it in README