How to transition an issue when a label is added?

Troy Barnes December 12, 2017

Hello, 

I have been searching the internet for a solution to a problem I am trying to tackle. I have come across a lot of different sites and input but nothing seems to really help me.

First, let me explain my problem at a high level. I have a team of developers that prefer to use GitLab to manage issues but I have a team of testers and product managers that prefer to manage issues in JIRA. I like to believe in win-win scenarios so I said "Why not both?" 

I found a tool from unito.io that will sync issues between Gitlab and JIRA but the problem I am facing is that Gitlab doesn't have a concept of a workflow or statuses other than open/close. Gitlab primarily uses labels. What I am thinking about doing is: If a JIRA issue has been updated with a label==foo then transition JIRA issue from phase 1 to phase 2.

I have found that my JIRA instance (it is the enterprise edition that my company maintains, not the cloud edition) has the Script Runner plug in enabled. Based on documentation I have found on the web I believe it is possible to create my own groovy class as a custom listener and configure it to happen on issueUpdate in my specific project. This is perfect. I can write some code that will check to see what the update was and what phase it is in. If it was a label that was added I can check to see if it is the label I am interested in and if it is and the issue is in the correct phase I can issue a command to transition the issue to the correct phase.

The problem is the documentation is really hard for a newbie like me to understand. I am trying to read enough of https://scriptrunner.adaptavist.com to understand what to do but I am not finding what I need. The docs at https://docs.atlassian.com/software/jira/docs/api/7.3.7/ are massive and a newbie has no way to know what to look for. This site: http://igorpopov.io/2014/11/24/rocking-with-jira-script-runner/ makes it seem like I have to do a lot of stuff just to write this small script (it makes me think I have to compile the JIRA code? Woah!)

I am look for a simple class that I can add to my custom script listener, then I edit a bug, I check to see if my script made the changes I thought it would based on my code and if it works I add more code and if it doesn't I troubleshoot my code. Repeat.

I wrote a tome here. I am really hopeful that someone will read this question and

a) help me out with some sample code

b) explain that what I want to do is not possible and tell me why

c) offer up some alternative solutions to my problem.

 

Thank you Atlassian Community, you are great!

3 answers

1 accepted

1 vote
Answer accepted
Gaston Valente
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 13, 2017

Troy,

You can achieve this with an script listener:

  1. You need to create an script listener to capture the ISSUE_UPDATED event
  2. In the code you check for a label placed by your other app
  3. You execute the transition using this code sample:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.query.Query
import com.atlassian.query.order.SortOrder
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.config.SubTaskManager
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
import org.apache.log4j.Logger
import org.apache.log4j.Level

def issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()


def transitionValidationResult = issueService.validateTransition("user", issue.getId(), "transitionId", issueInputParameters);
if (!transitionValidationResult.isValid())
{
String result = transitionValidationResult.getErrorCollection()
throw new Exception(result);
}

def transitionResult = issueService.transition(runAs, transitionValidationResult)

if (!transitionResult.isValid())
{
String result = transitionResult.getErrorCollection()
throw new Exception(result)
}

4. If everything is ok you need to clean the label field to prevent a loop

Troy Barnes December 13, 2017

OMG, @Gaston Valente thanks for the response!! I am going to try this today and see if I can get it to work.

Troy Barnes December 13, 2017

@Gaston Valente, I copied and pasted your code into my script console and I get some errors.

For this line of code:

def transitionValidationResult = issueService.validateTransition("user", issue.getId(), "transitionId", issueInputParameters);

I get the following errors from the static type checking inside the browser:

The variable [issue] is undeclared.

Since I am so new to this groovy programming I am not sure how this is supposed to be but I don't see

def issue = [something here]

anywhere in your code where you define the variable. Does that need to be added? If so what does it need to be assigned to? If not is it supposed to be assigned automatically based on one of you imports? I see 

import com.atlassian.jira.issue.Issue

But I don't see that assigned to a variable.

and for that same line of code another error from the static type checking:

Cannot find matching method for com.atlassian.jira.bc.issue.IssueService#validateTransition...
Please check if the declared type is right and if the method exists.

I have even more error but I think they are all related.

Troy Barnes December 13, 2017

In case it matters the version of JIRA I am on is  v7.3.7#73018-sha1:17f7a2e. Also, the version of Adaptavist ScriptRunner for JIRA is 5.1.6.

Gaston Valente
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 13, 2017

Troy,

You should obtain the issue from the event or by querying.

Check for the signature of the method validateTransition

you need to replace user and transitionId with the correct values:

users needs to be an instance of ApplicationUser

transitionId is an int, the id of the transition you need to execute

Troy Barnes December 13, 2017

I figured out how to get the issue, from the event object. I am now getting the 

def transitionValidationResult = issueService.validateTransition(user, event.issue.getNumber(), transitionId, issueInputParameters)

to work. The problem is that the code throws and exception:

java.lang.Exception: Errors: {}
Error Messages: [You can not transition a null issue.]

 So this says to me that I have to do something other than create an empty issueInputParameters. I checked the documention for issueService.newIssueInputParameters() and it doesn't do a good job of explaining what I need to do. How do I init this issueInputParameters object for my needs?

Steven F Behnke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 14, 2017

Don't use event.issue.getNumber, use event.issue.id

0 votes
Troy Barnes December 15, 2017

I have working code! For those of you that don't like spurious (unneeded) import statements you don't need any of the imports that Gaston has in his code. 

Here is what my code looks like:

import com.atlassian.jira.component.ComponentAccessor

// Get the instance of the IssueService because IssueService is the class
// that we use to perform "transition" operations in JIRA with Issues.
def issueService = ComponentAccessor.getIssueService()

// Initiate a new IssueInputParameters object with no initial values
def issueInputParameters = issueService.newIssueInputParameters()

// Define the user that will be making the transition
def user = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser()

// This is the ID of the transition that should happen. This can be seen as the transitions (id) when you edit workflow
// 91 is the id for "Reopen" since I am testing with a closed issue
def transitionId = 91

// This is the ID of the issue that triggered the event. This is the issue we will modify if applicable.
def issueId = event.issue.id

// To see if you have valid values log it
log.debug issueId

// OK, lets see if we have valid transition info
def transitionValidationResult = issueService.validateTransition(user, issueId, transitionId, issueInputParameters)

// Throw an exception if we didn't get what we wanted
if (!transitionValidationResult.isValid())
{
String result = transitionValidationResult.getErrorCollection()
throw new Exception(result);
}

// If we made it this far then we have the right values, let's transition the issue
def transitionResult = issueService.transition(user, transitionValidationResult)

if (!transitionResult.isValid())
{
String result = transitionResult.getErrorCollection()
throw new Exception(result)
}
0 votes
Gaston Valente
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 14, 2017

Hi, as Steve pointed out, you need to use getId

let us know if you have any another issue

sorry for the delay

Troy Barnes December 15, 2017

Hey, that worked! I tried event.issue (without the '.id') and I was getting an error about the type not matching. 

Suggest an answer

Log in or Sign up to answer