Make transition in listener

aas March 16, 2022

Hello community!

I try to make transition in scriptrunner custom listener, but I have 

It seems that you have tried to perform a workflow operation (251) that is not valid for the current state of this issue (XXXXXX). The likely cause is that somebody has changed the issue recently, please look at the issue history for details.

Event is Issue Created
I check workflow and there is a transition with id 251 (which i need)
My custom listener
0.png

if (summary.contains("Invalid item requisites")){
IssueService issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()
def user = ComponentAccessor.getUserManager().getUserByName('admin')
def transitionValidationResult = issueService.validateTransition(user, issue.id, 251, issueInputParameters)
if (transitionValidationResult.isValid()) {
def transitionResult = issueService.transition(user, transitionValidationResult)
if (transitionResult.isValid()){
log.warn("Transitioned issue ${event.issue.key}")
} else {
log.warn("Transition result is not valid")
log.warn(transitionResult.errorCollection)
}
} else {
log.warn("The transitionValidation is not valid")
log.warn(transitionValidationResult.errorCollection)
}
}

 

  But when I try to make that transition like that  

1.png

2.png

Everything works fine. 
Why I can't make transition in custom listener?

2 answers

0 votes
aas August 31, 2022

I've found solution. Like @Alexey Matveev answered here I've made different thread to move the issue to a different status and it works, but I really don't understand why I need another one thread
A post-function script:

import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueInputParameters
import org.apache.log4j.Logger

def user = ComponentAccessor.getUserManager().getUserByName('admin')
Thread newThread = new Thread(new MyThread(user, issue, transitionId, log))
newThread.start()

public class MyThread implements Runnable{
private Logger logger
private ApplicationUser threadUser
private Issue issue
private int transitionId

public MyThread(final ApplicationUser user,
final Issue issue, int transitionId, Logger logger){
this.threadUser = user
this.issue = issue
this.transitionId = transitionId
this.logger = logger
}

public void run(){
logger.info("Run method in MyThread")
MyThread.sleep(500) // delay for indexing
IssueService issueService = ComponentAccessor.getIssueService()
MutableIssue currentIssue = ComponentAccessor.getIssueManager().getIssueObject(issue.getId())
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters()
issueInputParameters.setSkipScreenCheck(true)
def transitionValidationResult = issueService.validateTransition(this.threadUser, currentIssue.getId(), this.transitionId, issueInputParameters)
if (transitionValidationResult.isValid()) {
logger.info("transitionValidationResult is valid")
def transitionResult = issueService.transition(this.threadUser, transitionValidationResult)
if (transitionResult.isValid()){
logger.info("Transitioned issue ${issue.key} through action ${transitionId}")
} else {
logger.warn("Transition result is not valid")
logger.warn("${transitionResult.errorCollection}")
}
} else {
logger.warn("The transitionValidation is not valid")
logger.warn("${transitionValidationResult.errorCollection}")
}
}
}
0 votes
Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 16, 2022

Hi @aas ,

your code seems correct! Two simple questions :

  • Did u try to perform a reindexing at project level to make sure that your issue is in the correct status where 251 action is available?
  • Are u sure that user used in the listener has transition permission and that 251 action has not conditions that affect your to do that?

Hope this helps,

Fabio

aas March 16, 2022

Hi @Fabio Racobaldo _Herzum_ ,

1. Do you mean that I need perform a reindexing somewhere in script listener? Smth like this?

boolean wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(issue)
ImportUtils.setIndexIssues(wasIndexing)

I've tried to add this code in custom listener before making transition but no luck.

2. User has all permissions. I can make transition in created issue manually via web interface.

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 17, 2022

Hi @aas ,

1. Please complete a manual reindex at project administartion level in order to be sure that issue is in the correct status.

2. Please check if that transition has some conditions that impact your code.

aas March 18, 2022

Hi @Fabio Racobaldo _Herzum_ ,

1. When you tell about manuall reindex you mean that?

3.png

I've done it, nothing has changed (

2. There're no any conditions that impact my code.

The only way my code works is using post-function. I've added another one transition 

4.png

And added Custom script post-function there. In that way script works and issue makes transition.

May be there no any available transition while issue creation so it tells that transition is not valid? I suppose issue must be created and only after that we can make transition or the logic is wrong? But how works fast-track transitions?

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 18, 2022

Please could you share your workflow in text view?

aas March 18, 2022

123.jpg

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 19, 2022

Hi @aas ,

all seems correct. Please could you share your whole listener code?

aas March 21, 2022
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.workflow.WorkflowManager
import com.atlassian.jira.workflow.JiraWorkflow
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils

def eventTypeManager = ComponentAccessor.getEventTypeManager()
def eventTypeName = eventTypeManager.getEventType(event.eventTypeId).getName()
log.warn("Event " + eventTypeName)

def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
log.warn("User " + user.name)

def issueManager = ComponentAccessor.issueManager
def issue = issueManager.getIssueObject(event.issue.key)
log.warn("Issue " + issue.key)

def summary = issue.getSummary()
if (summary.contains("Somr string")){
def cf= ComponentAccessor.getCustomFieldManager().getCustomFieldObject("customfield_33320")
// find 1 level option
def fieldConfig = marsrut.getRelevantConfig(issue)
def op_1 = ComponentAccessor.optionsManager.getOptions(fieldConfig)?.find { it.toString() == "Option name"}
// find 2 level option
def op_2 = op_1.getChildOptions().find{it.value == "Option name"}
List<Option> list = new ArrayList()
list.add(op_1)
list.add(op_2)
cf.updateValue(null, event.issue, new ModifiedValue(null, list), new DefaultIssueChangeHolder())
reIndexIssue(issue)
validateAndTransition(251, issue)
} else {
log.warn("summary not contains \"Invalid item requisites\"")
}

def reIndexIssue(Issue issue){
//Re-index the issue after update
log.warn("Try index issue...")
boolean wasIndexing = ImportUtils.isIndexIssues()
log.warn("wasIndexing " + wasIndexing)
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(issue)
ImportUtils.setIndexIssues(wasIndexing)
}

def validateAndTransition(int transitionId, Issue issue){
IssueService issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()
def user = ComponentAccessor.getUserManager().getUserByName('username')

def transitionValidationResult = issueService.validateTransition(user, issue.id as long, transitionId, issueInputParameters)
if (transitionValidationResult.isValid()) {
log.warn("transitionValidationResult is valid")
def transitionResult = issueService.transition(event.user, transitionValidationResult)
if (transitionResult.isValid()){
log.warn("Transitioned issue ${event.issue.key} through action ${transitionId}")
} else {
log.warn("Transition result is not valid")
log.warn(transitionResult.errorCollection)
}
} else {
log.warn("The transitionValidation is not valid")
log.warn(transitionValidationResult.errorCollection)
log.warn(transitionValidationResult.toString())
}
Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 21, 2022

Hi @aas ,

I don't understand the following portion of code

cf.updateValue(null, event.issue, new ModifiedValue(null, list), new DefaultIssueChangeHolder())

If you want to update some cf within your issue you should use the updateIssue method provided by IssueManager https://docs.atlassian.com/software/jira/docs/api/8.19.0/com/atlassian/jira/issue/IssueManager.html#updateIssue-com.atlassian.jira.user.ApplicationUser-com.atlassian.jira.issue.MutableIssue-com.atlassian.jira.issue.UpdateIssueRequest-

Could you try to test your listener without  the following lines?

 def cf= ComponentAccessor.getCustomFieldManager().getCustomFieldObject("customfield_33320")
// find 1 level option
def fieldConfig = marsrut.getRelevantConfig(issue)
def op_1 = ComponentAccessor.optionsManager.getOptions(fieldConfig)?.find { it.toString() == "Option name"}
// find 2 level option
def op_2 = op_1.getChildOptions().find{it.value == "Option name"}
List<Option> list = new ArrayList()
list.add(op_1)
list.add(op_2)
cf.updateValue(null, event.issue, new ModifiedValue(null, list), new DefaultIssueChangeHolder())
reIndexIssue(issue)
aas March 21, 2022

Hi @Fabio Racobaldo _Herzum_ ,

What's wrong with that code? It's simple update of custom field

cf.updateValue(null, event.issue, new ModifiedValue(null, list), new DefaultIssueChangeHolder())

 There're many examples of using it:
https://library.adaptavist.com/entity/update-the-value-of-a-custom-field-using-a-listener


https://community.atlassian.com/t5/Agile-articles/Three-ways-to-update-an-issue-in-Jira-Java-Api/ba-p/736585

 

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 22, 2022

Hi @aas ,

did u try without that part of code?

aas March 22, 2022

Yes, I've tried, has the same error 

It seems that you have tried to perform a workflow operation (Assigned to FD) that is not valid for the current state of this issue (**-*****). The likely cause is that somebody has changed the issue recently, please look at the issue history for details.

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 22, 2022

Could you try to update the step name from "New issue" to "Created" so it matches the associated status?

aas March 23, 2022

Hi @Fabio Racobaldo _Herzum_ 
Changing step name hasn't helped, unfortunately.
I supoose there are no transition with id 251 in the moment of issue craeation 

2.png

Fast Track Transition, I think, firstly create issue and only after that (when issue in Created status) try to make transition

Fabio Racobaldo _Herzum_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
March 24, 2022

Hi @aas ,

I don't figure out the reason. Listener works after creation because it start after that create event is fired as last post function in creation. Based on that, when listener works, issue should be created and in correct status.

Stefan Stadler February 21, 2024

Hi @Fabio Racobaldo _Herzum_ 

I can confirm that this behaviour is still present.

Did you in the meantime figure out, why this is happening? I am suspecting some kind of a race condition with Jira Logic of how Listeners are working. When running the transition in a separate thread, the transition works. However, this has other side effects.

It would be great if there had been some insight in the meantime :)

Thanks!

aas February 21, 2024

Hi @Stefan Stadler 

What side effects are you talking about?

 

Stefan Stadler February 21, 2024

Hi @aas 

in our case, I am creating an approval issue by this transition. And as a service user shall be used to create those (for compliance reasons), I can't use fast track.

So, I have tried to transition the issue as described above and always get the error "

It seems that you have tried to perform a workflow operation..."

So the transition fails at validation, which is the behaviour you described above.

Running the exact same code for the exact same issue is working fine when it is run from the ScriptRunner script console. No issue arrises with that, so I assume the code itself is correct.

I have then tried to use the Thread.start method. Now the transition is working.

However, it seems that the issue creation in the new thread is causing some inconsistency with the new issue. I cannot approve the issue, i.e. the approval button is not available - neither within Jira nor in the Service Desk view.

Strange enough, a simple update of ANY field is causing the approval to be working again, so I suspect some kind of internal issue with Jira. I tried with reindexing that issue, but that does not help. It has to be any update.

So I decided to execute the transition in a new thread from script console.

Here the same behaviour occurs:

Without a new thread, the transition and creation of code works (but cannot be done from the post function).

With the new thread, an approval is not possible until a manual update on the issue is done.

As I do not have a technical explanation here, I am sticking again to creating an event as a specific user and catch the event with a fast-track listener.

This will lead to a huge amount of fast-track listeners at some point in time as each can only be used for one specific transition.

Hope this makes the issue clear.

Stefan L_ March 22, 2024

Hy guys,

@Stefan Stadler is it possible that your postfunction order is incorrect? 
The Post Function must not be set to rank 1 in the order.
I had similar issues in the past.

@aas For further help a use case would be great, because I'm also using "Listener" and it is towkring as expected by Event "Commented"

Adding Scriptrunner Version would also help ;-)

Regards Stefan

Stefan Stadler March 22, 2024

Hi @Stefan L_ 

the post function definitely has not been on position 1 in the order. I have moved it after the Fire Event post function, similar to how the ScriptRunner fast-track post function has been set.

It is very strange. I personally get the best events when firing a specific event and this event triggers a ScriptRunner fast-track listener. So I have to setup multiple fast-track listeners for different conditions. This is very inefficient from an administrative perspective, but works best from a functional perspective.

Stefan L_ March 22, 2024

Ah Ok I understand your problem. I only have 2 Fast Track listeners as global listernes not inside of the workflow.
I only use "post function script" currently and this is working but is placed between Create and fire event.

What helps a lot are some log outputs to verify the status.

Maybe you can add two scriptrunner scripts (one before and one after fire event) with log output to check some stuff which is part of your condition.

It's possible that you find a small gap between "AS IS" and "SHOULD BE" 🤷‍♂️

With this way I found a lot of scripting errors due to missing values for my conditions.

Stefan Stadler March 22, 2024

@Stefan L_ 

may I know an example on how you setup the issue transitions within the post functions? Maybe show a quick screenshot of the order and the script? Maybe I am also doing something wrong, which I did not be aware so far...

Thanks for your help!

Stefan L_ March 22, 2024

Sure

That is my "Script Post-function"

2024-03-22 14_44_21-Transition_ Create - MIC Customs Solutions.png

Any my global listeners.

2024-03-22 14_45_58-Listeners.png


As I mentioned - I don't use "Fast transition" at a workflow.
Maybe you must define your Listener entries "more globally" - could also help.

Like Stefan Stadler likes this

Suggest an answer

Log in or Sign up to answer