I have been exploring how to automatically resolve a created issue as long as it meets a specific set of criteria. (ex. duplicate ticket is already open for the issue etc) This has been more complicated to accomplish than we originally thought, but we have been making progress up until now. I am using a groovy script listener to process an Issue Created event, look up similar issues and then decide if the issue needs to be resolved as duplicate if there are similar issues. Everything works up to the issue transition phase.
The logs tell me that "It seems that you have tried to perform a workflow operation (Resolve this issue) that is not valid for the current state of this issue (DESK-16). The likely cause is that somebody has changed the issue recently, please look at the issue history for details."
I have done my due diligence and validated that the issue I am referencing is in a status that allows this transition/step, but no matter what I do I cannot get the issue the transition immediately after it is created. I played with timing by delaying the operation by a few seconds, but that doesn't fix it. I also decided to attempt this operation outside of the issue creation event and it works perfectly fine. By throwing an event in a workflow transition, not issue creation, the transition is performed as expected. Code to perform the transition, taken out of context:
// Transition Issue def issueService = ComponentAccessor.getIssueService(); IssueInputParameters transitionParams = issueService.newIssueInputParameters(); //Construct the transition details transitionParams.setResolutionId("3"); //Resolution "Duplicate" transitionParams.setComment(comment); //Comment to leave transitionParams.setStatusId("4"); //Status of "Resolved" transitionParams.setSkipScreenCheck(true); int transitionId = 801; //Action "Resolve this issue" //Check the validity of the constructed transition log.info("Checking Transition..."); TransitionValidationResult transitionValidation = issueService.validateTransition(appUser, triggeringIssue.getId(), transitionId, transitionParams); if (transitionValidation.isValid()) { //The transition is valid //Create a worker thread that waits for 2 seconds before performing the transition. ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); Runnable runnableUpdate = new Runnable() { @Override public void run() { IssueResult transitionResult = issueService.transition(appUser, transitionValidation); log.info("Issue ${triggeringIssue} Transitioned"); } }; worker.schedule(runnableUpdate, 2, TimeUnit.SECONDS); log.info("Worker Created"); } else { //Transition is invalid ErrorCollection errors = transitionValidation.getErrorCollection(); if (errors.hasAnyErrors()) { Collection<String> errorMessages = errors.getErrorMessages(); for (Iterator<String> errorsIter = errorMessages.iterator(); errorsIter.hasNext();) { log.info(errorsIter.next()); } } log.info("Current Issue Status: ${triggeringIssue.getStatusObject().getName()}"); log.info("Triggering User Name: ${triggeringUser.getName()}"); log.info("App User Name: ${appUser.getName()}"); log.info("Transition is Invalid"); }
In an effort to try and make this work, I tried throwing a custom event instead of transitioning the issue from within the Issue Creation to try and separate the steps. (Maybe the issue hasn't actually been created yet until the Issue Created event finishes) I then created a new listener to process the new custom event and perform the transition from there. The "Issue Created Listener" gets the issue created event, processes it, and throws my custom test event perfectly fine, but the new listener does not get this event. I have opened up the "Test Event Listener" to listen for all event from all projects and then continue only if the eventID matches my custom event. I can see it logging that it catches all events EXCEPT for the custom event I am throwing from the other listener. When the custom "Test Event" is thrown from a workflow transition, the listener works as expected. Code to throw custom event:
log.info("Constructing New Event..."); IssueEventManager issueEventMan = ComponentAccessor.getIssueEventManager(); //IssueEvent newEvent = new IssueEvent(triggeringIssue, event.getParams(), triggeringUser, 10000L); IssueEventBundleFactory issueEventFactory = (IssueEventBundleFactory) ComponentAccessor.getComponent(IssueEventBundleFactory.class); Comment eventComment = (Comment) ComponentAccessor.getComponent(Comment.class); IssueEventBundle eventBundle = issueEventFactory.createWorkflowEventBundle(10000L, triggeringIssue, appUser, eventComment, null, event.getParams(), true, ""); //IssueEventBundle eventBundle = issueEventFactory.wrapInBundle(newEvent); //Create a worker thread that waits for 2 seconds before throwing the new event ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); Runnable runnableUpdate = new Runnable() { @Override public void run() { //Fire new event log.info("New Event Thrown"); issueEventMan.dispatchEvent(eventBundle); } }; worker.schedule(runnableUpdate, 2, TimeUnit.SECONDS); log.info("Worker Created");
Logs:
[com.opticalphusion.DuplicateIssueListener] Event: 1 fired for DESK-32 and caught by com.opticalphusion.DuplicateIssueListener [com.opticalphusion.NewCustomerIssueListener] Event: 1 fired for DESK-32 and caught by com.opticalphusion.NewCustomerIssueListener [com.opticalphusion.NewCustomerIssueListener] Checking JQuery Statement... [com.opticalphusion.NewCustomerIssueListener] JQuery Statement is Valid [com.opticalphusion.NewCustomerIssueListener] Looking for Similar Issues... [com.opticalphusion.NewCustomerIssueListener] Similar Issues Found! DocumentIssueImpl[issueKey=DESK-5] [com.opticalphusion.DuplicateIssueListener] Event: 6 fired for DESK-32 and caught by com.opticalphusion.DuplicateIssueListener [com.opticalphusion.NewCustomerIssueListener] Linking Issues... [com.opticalphusion.NewCustomerIssueListener] Constructing New Event... [com.opticalphusion.NewCustomerIssueListener] Worker Created [com.opticalphusion.NewCustomerIssueListener] New Event Thrown
So you can see that both listeners are processing the "Issue Created" event and the secondary listener is seeing the "Issue Linked" event, but event 10000 is not caught by anything.
Am I missing something in order to make my groovy listener "hear" the event I am throwing from another groovy listener? Is there a way to log all events that are thrown so I can make sure the event is ACTUALLY happening?
Thank you very much!
Community moderators have prevented the ability to post new answers.
Doing it from the Create transition is super-tricky. Also, doing it in a thread is sub-optimal because the UI won't display the correct status.
I would try using the Fast-track built-in listener... or workflow function. Just try it without any condition. If that works, you could add your duplication detection to the Condition field. At least I'd like to see if that works, to rule out any other issues.
I did encounter the thread issue you are talking about when I was originally testing it outside of the Create transition, and I was able to get around it using a Runnable and scheduled worker thread. I originally investigated using the built-in listeners and scripts, but my condition to detect duplication is too complex and doesn't fit the expected use. The condition I need to use is dynamic and depended on the value of 3 custom text fields/drop downs in the issue that is being created. I need to perform a JQL search with a formula similar to the following: (pseudocode) def jqlSearch = 'project = DESK AND issuetype = "IT Help" AND status = "Waiting for support" AND "Serial Number" ~ ${triggeringIssue.serialnumber} AND "Location" ~ ${triggeringIssue.Location} AND "Device Type" ~ ${triggeringIssue.DeviceType}'; This will identify any issues that have the same location, device type, and serial number; as that is what will qualify the created issue as a duplicate. I do not believe I can perform such a query from within the condition section available to me for the built in scripts. Is this correct? The fast-track built-in listener is capable of transitioning the issue. I validated this, but it also does not receive the custom event that is thrown by my custom listener. If I can get my custom event (10000) to dispatch from my custom groovy listener then I think we can get this to work with either my other custom listener or the Fast-Track Built-In one. It just looks like the event isn't being thrown at all. In conclusion, IssueEventManager.dispatchEvent(eventBundle); is not working from within my groovy custom listener. Thank you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Looks very cool !! Just a shot in the dark.. Is there any compulsory fields that need a value upon Resolving?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
No. The only required field is the resolution code which I am setting. Otherwise, there are no required fields, no validators or conditions that require any fields nor any checks to make sure a field is set. To ensure that wasn't the problem I used transitionParams.setSkipScreenCheck(true); which is supposed to bypass any checks for required fields during a transition. (https://developer.atlassian.com/static/javadoc/jira/6.4.1/reference/com/atlassian/jira/issue/IssueInputParameters.html#setSkipScreenCheck(boolean)) To further validate that this is not the reason that the transition isn't allowed, the transition works perfectly fine outside of the initial Issue Created transition that occurs. It is almost as if the issue is in some sort of limbo state before it actually hits the first status in the workflow, but everything in the database says that it has finished the creation process. Thanks for the thought.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I guess its not an indexing issue either,.. @Nic Brough [Adaptavist] any thoughts buddy?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.