scriptrunner post function won't trigger

I have a basic understanding of why the function won't trigger, but I'm wondering if anyone can provide a workaround.

 

I have two different workflows: one for Epics and another for Requirements that exist as issues with Epics.

 

In the Requirements workflow, I have added a Linked Transition (JSU) post function on the transition to the Done status that will trigger a specific transition on its Epic when all of its siblings are in the Done status. Basically, whenever all issues within the Epic are Done, move the Epic to a Ready to Test status. I have confirmed that, on its own, this post function executes properly.

 

However, I have added a ScriptRunner workflow function on the transition in the Epic workflow that is being updated by the Linked Transition post function I mentioned above. This function checks a JQL query and requires that all issues within the Epic are in the Done status in order for the transition to be allowed. I have confirmed that this condition works properly on its own.

 

My problem is that, once I add the condition to the Epic workflow, the Linked Transition post function no longer executes. My guess is that there is a timing issue where the Epic isn't fully aware that the last issue in the Epic has transitioned to Done status, so it doesn't allow the transition to execute. Afterwards, I can manually transition the Epic to the Ready to Test status.

 

I have moved the Linked Transition post function all the way to the bottom of the list of post functions (after the issue is stored in the DB and re-indexed), but this doesn't have any effect. Is there a way that I can notify the Epic that all of its children are in Done status while the final child is transitioning to the Done status so the condition is met?

1 answer

1 accepted

1 vote
Jenna Davis Community Champion Dec 18, 2017

Hello, 

Could you send me screenshots of the two post-functions you've mentioned, as well as screenshots of your post-function order? It's a bit hard to visualize whats going on from just text. :)

Just as a quick recommendation, you might be able to use a fast-track transition listener instead of what you're currently trying with your Linked Transition. You could fire an event that the listener can catch, then the listener will transition your epic. There may be a better method, or I could've mis-read something and this won't work as I'm thinking, that's just what I thought of while reading. :)

Jenna

 Below are the two screenshots showing the Linked Transition position in the Requirement workflow as well as the details of the function itself.




On the Epic workflow, there is a condition on the transition that is being triggered by the Linked Transition shown above. The condition is met if the Epic matches the following JQL:


issueFunction not in epicsOf("'Backlog Tier' = 1 and 'status sequence' < 99")

To clarify, the status sequence is a two digit prefix to the status name that allows us to treat the status as a number. Backlog Tier is a scripted field that will return 1 if the Issue Type of the issue is one of the types we use as children of Epics (Requirement is one of these types).





Thank you for looking into this and let me know if there is anything else that you need.Post Function Order.PNGRequirement Post Function.PNG

Jenna Davis Community Champion Dec 18, 2017

What ScriptRunner function did you add on the transition that you mentioned in your first post? Can you send a screenshot of that as well? Sorry for all of the questions, I'm just trying piece together everything. 

Jenna

The transition on the Epic workflow doesn't have a post function, but just a script condition, which I'll include below (but it's the same as what I included above):

issueFunction not in epicsOf("'Backlog Tier' = 1 and 'status sequence' < 99")

 

There aren't any additional post functions on the transition in the Epic workflow (besides the generic ones that are included on all transitions).

Jenna Davis Community Champion Dec 19, 2017

Okay, gotcha. I think your original guess is correct as to why this isn't working, and I'm not sure of an immediate simple workaround for this. There could be an easy way that I'm missing, but I am not sure at the moment.

I would like to again suggest a Script Listener that would listen for an event (you'd probably want to create a custom event) from your stories, it then checks if they're all finished and transitions if they are. I think that this would work for you.

Please let me know if you have any more questions, I'm sorry I haven't been a ton of help!
Jenna

I've created a custom event on the original transition that triggers a Script Listener. I'm including the code here because it's still not working properly.

The listener gets triggered, but it does not transition the Epic. It goes through the point where it logs the "about to validate" message and then nothing else happens (the "validating" message is not returned.

 

import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.bc.issue.IssueService;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.bc.issue.IssueService.AssignValidationResult
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.issue.IssueInputParameters;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.event.type.EventDispatchOption;
//import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.web.bean.PagerFilter

 

MutableIssue getEpic(MutableIssue mutable, Logger log)
{

def fieldName = "Epic Link"
def manField = ComponentAccessor.getCustomFieldManager()
def field = manField.getCustomFieldObjectByName(fieldName)
if (field == null)
{
return;
}

def epic = mutable.getCustomFieldValue(field) as MutableIssue;
if (epic == null)
{
return;
}

return epic
}
def issue = event.issue as MutableIssue
def epic = getEpic(issue, log)

int transitionId = 151;
def user = event.user
def issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()

issueInputParameters.setSkipScreenCheck(true)

log.error("about to validate")
def validationResult = issueService.validateTransition(user, epic.id, transitionId, issueInputParameters)
log.error("validating")
if (validationResult.isValid() == false)
{
log.error("Could not transition task ${epic.key}, errors: ${validationResult.errorCollection}")
return
}

def issueResult = issueService.transition(user, validationResult)
log.error("result defined")
if (issueResult.isValid() == false)
{
log.error("Failed to transition task ${epic.key}, errors: ${issueResult.errorCollection}")
}
Jenna Davis Community Champion Dec 22, 2017

Try out this instead:

import org.apache.log4j.Logger
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.component.ComponentAccessor

MutableIssue getEpic(MutableIssue mutable, Logger log)
{

def fieldName = "Epic Link"
def manField = ComponentAccessor.getCustomFieldManager()
def field = manField.getCustomFieldObjectByName(fieldName)
if (field == null)
{
return;
}

def epic = mutable.getCustomFieldValue(field) as MutableIssue;
if (epic == null)
{
return;
}

return epic
}
def issue = event.issue as MutableIssue
def epic = getEpic(issue, log)

def transitionId = 151;
def user = event.user
def issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()
def transitionResult

issueInputParameters.setSkipScreenCheck(true)

log.error("about to validate")
def validationResult = issueService.validateTransition(user, epic.id, transitionId, issueInputParameters)
log.error("validating")

if (validationResult.isValid()) {
transitionResult = issueService.transition(user, validationResult)
if (transitionResult.isValid())
{ log.debug("Transitioned issue $epic through action $transitionId") }
else
{ log.debug("Transition result is not valid") }
}
else {
log.debug("The validationResult is not valid")
}

I didn't test this directly, so if something isn't working let me know. I may have made a mistake in there. :)

Jenna

Jenna,

I'm still seeing the same behavior.

It seems like the issue is related to the following line, since we log the message before the line, but not after.

def validationResult = issueService.validateTransition(user, epic.id, transitionId, issueInputParameters)

 

However, after looking at the method, it seems like everything is configured correctly (plus, no errors are being returned. I also added a log message before to confirm that the epic.id and transitionId contain the expected values, so I don't really know what's going on.

Jenna Davis Community Champion Dec 22, 2017

Would you be able to use the built-in fast-track transition script? It should handle the transitioning for you without having to write out the code as we're trying to here. 

Jenna

Is it possible to do that when the Requirement is being manually transitioned but I need to automatically transition the Epic? It seems like the fast-track transition script only generates an event that can be processed on the same issue.

Jenna Davis Community Champion Dec 22, 2017

Ahh, you are correct. My mistake. 

Here's is a working example of transitioning an issue, it will transition the issue if it is of type "Task". I've tested this out and can verify that it works:

import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueInputParametersImpl

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def issueManager = ComponentAccessor.issueManager
def issue = issueManager.getIssueObject(event.issue.key)
IssueService issueService = ComponentAccessor.getIssueService()
def actionId = 21 // change this to the step that you want the issues to be transitioned to
def transitionValidationResult
def transitionResult
def customFieldManager = ComponentAccessor.getCustomFieldManager()
log.debug("The issue type is: " + issue.getIssueType().name)

if (issue.getIssueType().name == "Task") {

transitionValidationResult = issueService.validateTransition(currentUser, issue.id, actionId,new IssueInputParametersImpl())

if (transitionValidationResult.isValid()) {
transitionResult = issueService.transition(currentUser, transitionValidationResult)
if (transitionResult.isValid())
{ log.debug("Transitioned issue $issue through action $actionId") }
else
{ log.debug("Transition result is not valid") }
}
else {
log.debug("The transitionValidation is not valid")
}
}

 I will work on figuring out the issue in your code, but I thought sending you a working example might help should you work on this before I'm able to get back to you. :)

Jenna

I've confirmed that, as long as I don't have the condition on the Epic workflow that checks to see if all of the issues in the Epic are in Done status, the transition works fine, both through the Linked Transition post function or the Script Listener.

 

Any thoughts as to why the Epic still thinks that the Issue is not in the Done status even though the post function is the last one I execute (after the post functions that store the Issue in the database and re-index)? That seems to be the root cause, as I added some additional logging and the validationResult.isValid() method is not true in the Script Listener.

Since the condition on the Epic transition seemed to be a deal breaker across the board, whether using a Script Listener or a workflow post function, I came up with the following solution.

 

I created a secondary transition into Ready for Test status on the Epic workflow. In this case, the only condition is that the user who executes the transition has to be part of a system accounts group (so it isn't visible to normal users).

Whenever any of the Epic's children transition into the Done status, they perform the Linked Issue Transition post function (basically what I had shown in a previous screenshot) that executes the new secondary transition and is executed by a user who is in the system accounts group.

 

It's not the most elegant solution, but it does work.

Suggest an answer

Log in or Sign up to answer
How to earn badges on the Atlassian Community

How to earn badges on the Atlassian Community

Badges are a great way to show off community activity, whether you’re a newbie or a Champion.

Learn more
Community showcase
Published Thursday in Confluence

Three common content challenges + how to manage them

An efficient enterprise content management system, or ECM, is a must-have for companies that create work online (cough   cough, all companies). If content calendars, marketing plans, and bu...

97 views 0 6
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you