Scriptrunner w/ Automation - Set Resolution

Timothy Ryner
Contributor
July 12, 2022

Seeking assistance writing a Scriptrunner script that would be invoked by a Project Automation Action to set/clear the Resolution.  We cannot use Post Functions or Transition Screens in our instance, since we have thousands of workflows all managed by our Project Admins (who remove & break transitions constantly).  Using a Global Project Automation Rule allows projects to decide how they want their Resolution set, so it can be enabled on a per-project basis.

The script could be written so that it sets the Resolution to "Done" or "None" based status category. The Rule could look like this:

  • Event:  Issue Transitioned
    • Condition: None 
      • Action:  Execute Script
        (which sets Resolution to Done or None based on Status Category)

Or, instead of putting it all in one script, I think I can also configure the rule with Conditions that trigger different scripts, one to set to Done, other to set to None.

The Project Automation Rule will look something like this:

  • Event:  Issue Transitioned
    • Condition: If {{issue.status.statusCategory}} equals "Done"
      • Action: Execute Script1
        (which sets Resolution to Done)
    • Condition: If {{issue.status.statusCategory}} does not equal "Done": 
      • Action: Execute Script2
        (which clears Resolution)
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor

def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()
iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '10000' : null

def result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors
def updatedIssue = is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false).issue

//updatedIssue.resolutionDate = new Date().toTimestamp()
//issue.store()

//update the JIRA index so that jql  for "resolved" work
//boolean wasIndexing = ImportUtils.isIndexIssues()
//ImportUtils.setIndexIssues(true)
//ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(updatedIssue)
//ImportUtils.setIndexIssues(wasIndexing)

Capture.PNG

Any help is greatly appreciated!

 



Final Update:

I was able to put together an Automation Rule/Scriptrunner Script combo that will edit the Resolution & Resolved date based on the Status Category.

I leveraged more of the Rule Conditions to make the scripts as simple as possible, since I'm not Scriptrunner savvy and don't want to create something I can't fix.  The scripts will probably evolve over time, but for now, it does what it needs to do.

 


Automation Rule Configurations:

  • Event:  Issue Transitioned
    • Condition:  (none)  
      • Action:  Execute Script
        Sets Resolution based on Status Category
        • Status Category = To Do, In Progress
          Resolution = None (null)
        • Status Category = Done
          Resolution = Done
  • Then, if:
    • Condition:  Status Category = To Do, In Progress
      • Action:  Execute Script
        • Set Resolved date = None (null)
  • Else if:
    • Condition:  Status Category = To Do, In Progress
      • Action:  Execute Script
        • Set Resolved date = Now

Set Resolution Rule Breakdown.PNG

Set Resolution Script:

import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor
import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()

iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '10000' : null

def result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors

def updatedIssue = is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false).issue
Set Resolved date to Now Script
import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issueMutable = issue as MutableIssue
def issueManager = ComponentAccessor.issueManager
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

issueMutable.setResolutionDate(new Timestamp(System.currentTimeMillis()))

issueMutable.store()

 

Set Resolved date to Null Script

import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issueMutable = issue as MutableIssue
def issueManager = ComponentAccessor.issueManager
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

issueMutable.setResolutionDate(null)

issueMutable.store()
Thanks again for the help @Peter-Dave Sheehan 
Hopefully this is helpful to others seeking to escape the administrative tyranny of Resolution configurations.

8 answers

2 accepted

3 votes
Answer accepted
Peter-Dave Sheehan
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.
July 12, 2022

I haven't done much with Automation.

So I'm not sure if each actions are expected to be independent, of if they are typically  cummulative  against an ephemeral issue object in memory and only persisted at the end of the automation rule execution (once all actions have been gathered).

But assuming you want the action to be 100% standalone, you could write a script like this:

import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor
def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()
iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId =
null

def result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors
is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false)

If you want to set the resolution instead of clear it, you can replace "null" with the id of the resolution you want to set (with double quotes).

Might as well put your logic in the script:

iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '11' : null

Resolution Done is "11" in my environment and

Status Category Done, actually has the name "Complete" and the value "Done" is fund under primaryAlias.

Timothy Ryner
Contributor
July 13, 2022

@Peter-Dave Sheehan ,

Thanks for getting me started, and apologies for not being more Groovy fluent so I can do some of my own troubleshooting.  Java/Groovy training is def in my future.  

Dropped it into the console and was greeted with some red.   Curious if you're on Cloud and I'm on Server?

I'll start with the "is."related ones:

Resolution Script.PNG

[Static type checking] - Cannot find matching method com.atlassian.jira.bc.issue.IssueService#validateUpdate(java.lang.Object, java.lang.Object, com.atlassian.jira.issue.IssueInputParameters). Please check if the declared type is correct and if the method exists.

[Static type checking] - Cannot find matching method com.atlassian.jira.bc.issue.IssueService#update(java.lang.Object, java.lang.Object, com.atlassian.jira.event.type.EventDispatchOption, boolean). Please check if the declared type is correct and if the method exists.

Again, appreciate the assistance!

Peter-Dave Sheehan
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.
July 13, 2022

Hi @Timothy Ryner 

I'm on server. I'm careful to check which questions I can safely answer or note based on the environment type since I don't have much experience with cloud.
The first 2 lines would not be possible at all on cloud. There is no access to any com.atlassian classes.

It's normal to see some red like this in the Script Editor (which looks to be where you are) and in the console.

In any given script context, ScriptRunner will include several variables in what's called the "binding" for the script. Which variables are available will depend on the content.

But in the script editor, there is no awareness of where the script will be used and what variables are expected to be available.

In all those context places, you will see a question mark icon below the script box that you can click to get the details of what variables are available.

In a project automation rule, you will see that "issue" and "currentUser" are both in the list.
Generally, "static type checking errors" are not always fatal. It's just the script editor telling you it can't predict and verify the code. 

Now that you've saved your script, you can go back to project automation rule and point an "execute script" block to that file. You should see the "green dot" indicating the script compiled fine in that context.

Timothy Ryner
Contributor
July 13, 2022

Great!  Thanks for the explanation, makes perfect sense!  Doing some testing now. 

I am noticing that the Resolved Date is not being set.  I know there is a bug with Automation with this exact behavior, but I was hoping that setting the Resolution by the script would negate that bug.   Does the script potentially bypass some of Jira's logic for setting the Resolved Date at the same time as Resolution, and thus an additional action needs to be included?

Resolution Script Troubleshooting.PNG

Peter-Dave Sheehan
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.
July 13, 2022

It looks like the only way to set this value is with an internal method that has some very strongly worded warning against using it

But if you want to disregard those warnings:

import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor


def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()
iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '11' : null

def
result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors
def updatedIssue = is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false).issue

updatedIssue.resolutionDate = new Date().toTimestamp()
issue.store()

//update the JIRA index so that jql  for "resolved" work
boolean wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(updatedIssue)
ImportUtils.setIndexIssues(wasIndexing)

It would be possible to combine setting the resolutionId and the date the same way,m but I felt that the littlest change using "store()" possible was best.

Timothy Ryner
Contributor
July 13, 2022

I think I'll heed the warnings, at least for now.  Here's where I have landed. 

  • Using your script with few edits:
    • update the ResolutionID (10000)
    • commented out the Resolution Date 
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor

def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()
iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '10000' : null

def result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors
def updatedIssue = is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false).issue

//updatedIssue.resolutionDate = new Date().toTimestamp()
//issue.store()

//update the JIRA index so that jql  for "resolved" work
boolean wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(updatedIssue)
ImportUtils.setIndexIssues(wasIndexing)

 

  • Made a wildly simply Automation Rule
    • Event:  Issue Transitioned
    • Action: Run Script
      • Set it to execute immediately, which sets the Resolution ahead of the page refresh, so the issue shows the new Resolution immediately.

Resolution Script Rule.PNG

 

  • Tested with an issue that has none of the standard Resolution configs 
    • Not a Simplified Workflow
    • No Post-functions
    • No Transition Screens for setting Resolution
      (did use transition screens for Comments, just to ensure those configs don't interfere)

Resolution Script Function.PNG

  • The Outcome
    • Resolution = Done when Status Category = Done.
    • Resolution = Unresolved (null) when Status Category != Done
    • Resolved Date is always None.

This is as-close to a solution as I think we can safely get.  The Resolved Date is a bummer, but setting the Resolution automatically is a great improvement for our admin team and our users.  I appreciate the help!!  I hope other folks also find this useful!!

Peter-Dave Sheehan
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.
July 13, 2022

FYI, you don't need the indexing block (last 4 lines) and corresponding imports if you are not updating the resolutionDate and storing the value.

The issueService.update() method will take care of indexing the changes presented in the issueInputParameter (iip)

Stefano Coletta October 24, 2024

Following line is wrong

issue.store() 

This is the correct one

updatedIssue.store()

And thank you for all the effort you put into this!

0 votes
Answer accepted
Timothy Ryner
Contributor
July 14, 2022

@Peter-Dave Sheehan ,

You've helped a bunch, so feel free to tap out if you don't want to go down this rabbit hole.  Not being able to set/clear the Resolved Date is pretty significant. 

But then how does Scriptrunner's bulk resolution edit built-in script work so effectively?  

I wonder if that script's configs can be located somehow, to see if there is any magic that can be replicated.

Timothy Ryner
Contributor
July 14, 2022

I swear, if Adaptivist were to rewrite that bulk resolution script into a standalone feature for project admins to use, give it similar functionality to what we're doing here, make it into a plugin, charge $10...they'd make hundreds of dollars!

Peter-Dave Sheehan
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.
July 14, 2022

One would need access to the scriptrunner sourcecode.

The .jar file includes a class you can attempt to decompile, but it won't make much sense.

It could very well be that under the covers, both the jira default functionality as well as the scriptrunner BulkFixResolution both use the issue.store() method. 

But even looking through the Jira source code, I don't understand why setting the resolution doesn't set the date correctly.

public void setResolution(Resolution resolution) {
final Resolution oldValue = getResolution();

this.resolution = resolution != null ? constantsManager.getResolution(resolution.getId()) : null;

modifiedFields.put(IssueFieldConstants.RESOLUTION, new ModifiedValue(oldValue, this.resolution));

if (resolution != null && resolution.getId() != null) {
String oldResolutionId = resolutionId;
resolutionId = resolution.getId();
//changing the resolution, also needs to update the resolution date.
if (!resolutionId.equals(oldResolutionId) || resolutionDate == null) {
setResolutionDate(new Timestamp(System.currentTimeMillis()));
}
} else {
resolutionId = null;
//if we are changing back to an unresolved state, also clear the resolution date.
setResolutionDate(null);
}

updateGV(IssueFieldConstants.RESOLUTION, resolutionId);
}

So it's a puzzle. One that I'm no longer attempting to solve.

Like Timothy Ryner likes this
Timothy Ryner
Contributor
July 27, 2022

I'm taking the convo to Adaptivist to see what they say. If I get useful info, you'll be the first to get it.  I'd love to see this puzzle solved.

Resolved Date set/clear via script triggered by Automation - ScriptRunner for Jira Server/Data Center - Service Desk (adaptavist.com)

Like Peter-Dave Sheehan likes this
Peter-Dave Sheehan
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.
July 29, 2022

So, did you get confirmation from Adaptavist that using the issue.store() is what they're doing in their built-in script behind the scene?

Timothy Ryner
Contributor
July 29, 2022

I indirectly asked, but the individual who assisted me didn't divulge, and instead focused on my script.

I'll inquire more-directly, maybe they'll show a little of how the fudge is made.

Peter-Dave Sheehan
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.
July 29, 2022

For the record, I can't view the ticket you linked above.

But if they validated your script that contains

issueMutable.setResolutionDate(null)
issueMutable.store()

Without raising a warning similar to what's in the documentation, then that's good enough for me.

Timothy Ryner
Contributor
July 29, 2022

I knew you wouldn't be able to get to my support request.  Thought I'd provide it anyway, and if you wanted to see it, you'd pass over your email so I could share it.  :)

 

And I asked more-directly.  We'll see what they say!

Timothy Ryner
Contributor
August 12, 2022

Adaptivist's response.  They were great to work with.  

If I had more dev chops, I'd decompile, but I'm good for now.  Thanks again for the help!

Great, glad to know I can help with the requirement.

Related to the built-in "Bulk Fix Resolutions", you can attempt to decompile class in the jar file. You will be able to access the code accordingly. It seems to me the script design is similar. However, keep in mind that the built-in script only runs once, and the approach you are taking will require to trigger every time the event is triggered. It seems to me Atlassian is trying to prevent the user from setting the resolution date programmatically as it may impact the issue consistency. For example, the user can set a resolution date while the issue is still in progress.

Unfortunately, I have tested on my end using a specific approach. Still, it does not update the Resolution date, and the other method uses a different format to set the Resolution date value. For the API usage, it is provided by Atlassian to update the issue, and we have a limitation that we can proceed with Jira API. If you are looking for a different method, my suggestion would be to reach out to Atlassian support if they can suggest the usage of the API.

I hope this answers your questions. Please let me know if you have further questions

Like Peter-Dave Sheehan likes this
1 vote
Timothy Ryner August 31, 2023

It's been running for a solid year now, quick update:

  • We're on Jira Server v9.10, still going strong.  It's running wonderfully, very few hiccups.
  • When we first went to v9, I did need to add a line to the script to trigger the single issue reindex, because that stopped happening as part of the issue update.  
  • No teams have come back and asked for more Resolution configurations: no workflow post functions, no screens, no nothing.  Hundreds of projects and thousands of users, no one appears bothered in the slightest.  In fact, everyone appreciates that the Resolution is reliably set.
  • The number of requests to the Jira team dropped significantly while the number of users consistently rises.  So doing more with less.
  • Using automation to trigger has been good and bad.
    • Good, because I can easily see performance stats on it running, and I can get email notifications when there are issues.
    • Bad, because sometimes it runs so much that it gets throttled, so I needed to break it up into two rules - Set Resolution Rule and Clear Resolution Rule. 

I'm going to start a yearly tradition of mailing Peter roses and chocolates on Valentines Day, because I'm in love with this script.  Thanks again!

1 vote
Timothy Ryner May 22, 2023

Since going to Jira v9.7, started having a problem with issues not indexing after being updated. The values were being written, but the issues weren't being indexed, so it would appear that hundreds of issues a day were not being updated when in fact they were.  For a few days, I just ran the background reindex a bunch of times to keep issues current while I figured out a solution.

I've made a couple changes:

  • I've redone the Automation Rule a little bit so the scripts aren't triggered for every transition.  Instead, the scripts get triggered when they need to be.
    • If a transition goes to the same status category, no need to run the script. 
  • Added another small script to the Automation Rule that runs a reindex on the single issue after the Resolution & Resolved Date are set.  

Reindex script:

import com.atlassian.jira.event.issue.AbstractIssueEventListener;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.util.ImportUtils;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.issue.index.IssueIndexingService;

 MutableIssue mutableIssue = ComponentAccessor.getIssueManager().getIssueObject(issue.getKey());
 boolean isIndex = ImportUtils.isIndexIssues();
 ImportUtils.setIndexIssues(true);
 IssueIndexingService IssueIndexingService = (IssueIndexingService) ComponentAccessor.getComponent(IssueIndexingService.class);
 IssueIndexingService.reIndex(mutableIssue);
 ImportUtils.setIndexIssues(isIndex);

After these changes, the Auto Resolution Rule is killing it!  I haven't had to modify a single workflow post function in almost 12 months, which has saved a significant amount of admin time!  

Still one of the best things I've ever implemented on our instance!  

0 votes
Aisha M
Contributor
October 9, 2024

Hi @Timothy Ryner , I am lookin for something similar. Would you be able to suggest if your script could be used for my use case. 

So, I want to run a script from the console to move an issue back from DONE to the IN PROGRESS status. Though I am able to achieve this, the Resolution of the issue still shows as 'done". .  No matter what, the issues moves to the "In progress' status but the resolution is still showing as 'done"

0 votes
Timothy Ryner
Contributor
December 6, 2022

A couple new notes:

Special "Automation" Account

I needed to create a special 'automation' account that has enough permissions in all projects to execute the rule/script, instead of tying it to my account.

A Few Errors

The scripts/rules run thousands of times per day, and I see about 3 errors per day on average (usually on the same issue being moved around a lot on the same day).

Those errors are always related to some issue configuration that doesn't seem like it should impact the script, but it does for some reason.  I've not looked into this further.

Most of the time, the error doesn't affect the outcome. 
Since the script runs even for every transition, even if the transition is to the same status category (To Do category > To Do category), the Resolution wouldn't change anyway, even if the script was successful.  So I'm good with the error in this instance.

Error Workaround

The only time it affects the outcome is when a transition would have resulted in a change to the Resolution.  My workaround for now is to run the Scriptrunner Bulk Resolution script.  I have 2 filters that I run against about once per week, edits 1 or 2 issues.  To me, this is very low up-keep, compared to how much time I spent modifying workflows previously, so it's acceptable for now.

Error Examples

Taken from the Audit Log of the Automation Rule that runs the scripts:


Action details:

Execute a ScriptRunner script action for Automation for Jira

Unexpected error executing rule:
{components=Components <scrubbed component name> are not valid for project '<scrubbed project name>'.}. Expression: result.valid
Script function failed on Automation for Jira rule:
Script function failed on Automation for Jira rule: **AUTO RESOLUTION RULE**, file: Auto Resolution Scripts/Set_Resolution.groovy, error: java.lang.AssertionError: {components=<scrubbed component name> are not valid for project '<scrubbed project name>'.}. Expression: result.valid
Action details:

Execute a ScriptRunner script action for Automation for Jira

Unexpected error executing rule:
{issuetype=Issue type is a sub-task but parent issue key or id not specified.}. Expression: result.valid
Script function failed on Automation for Jira rule:
Script function failed on Automation for Jira rule: **AUTO RESOLUTION RULE**, file: Auto Resolution Scripts/Set_Resolution.groovy, error: java.lang.AssertionError: {issuetype=Issue type is a sub-task but parent issue key or id not specified.}.
Expression: result.valid
Action details:

Execute a ScriptRunner script action for Automation for Jira

Unexpected error executing rule:
{customfield_10504=User '<scrubbed username>' is not valid for this user picker.}. Expression: result.valid
Script function failed on Automation for Jira rule:
Script function failed on Automation for Jira rule: **AUTO RESOLUTION RULE**, file: Auto Resolution Scripts/Set_Resolution.groovy, error: java.lang.AssertionError: {customfield_10504=User '<scrubbed username>' is not valid for this user picker.}. Expression: result.valid
0 votes
Timothy Ryner
Contributor
November 7, 2022

Three months later, and I can say that this has been wildly successful. 

The auto-resolution script runs around 7000 times a day, happily setting/clearing the Resolution & Resolved date.  I created a new field "Outcome", populated it with the same field options as Resolution, copied over the values globally, and placed it on the transition screens in place of Resolution. 

  • No customers are throwing a fit.  If you didn't know better, the system now functions how you'd think it would, and wouldn't suspect that it's a customization.
  • It passively fixed dozens of project workflows that were missing the post-function configurations to set the Resolution.  So instead of having thousands of issues with missing Resolutions, they're always set, regardless of how many configuration changes the Project Admins make to their workflows.
  • Customers can still capture data granularity with the "Outcome" field, just as they could before with Resolution (and with more options, because Project Admins can edit their field options).
  • Significant time has been freed up for the Admin Team not having to reconfigure workflows or teaching people about Simplified Workflows.

So again, I appreciate the help!  

0 votes
Timothy Ryner
Contributor
July 29, 2022

Final Update:

I was able to put together an Automation Rule/Scriptrunner Script combo that will edit the Resolution & Resolved date based on the Status Category.

I leveraged more of the Rule Conditions to make the scripts as simple as possible, since I'm not Scriptrunner savvy and don't want to create something I can't fix.  The scripts will probably evolve over time, but for now, it does what it needs to do.

 


Automation Rule Configurations:

  • Event:  Issue Transitioned
    • Condition:  (none)  
      • Action:  Execute Script
        Sets Resolution based on Status Category
        • Status Category = To Do, In Progress
          Resolution = None (null)
        • Status Category = Done
          Resolution = Done
  • Then, if:
    • Condition:  Status Category = To Do, In Progress
      • Action:  Execute Script
        • Set Resolved date = None (null)
  • Else if:
    • Condition:  Status Category = To Do, In Progress
      • Action:  Execute Script
        • Set Resolved date = Now

Set Resolution Rule Breakdown.PNG

Set Resolution Script:

import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.component.ComponentAccessor
import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def is = ComponentAccessor.issueService
def iip =is.newIssueInputParameters()

iip.setSkipScreenCheck(true) //only need this if the resolution is not editable
iip.resolutionId = issue.status.statusCategory.primaryAlias == 'Done' ? '10000' : null

def result = is.validateUpdate(currentUser, issue.id, iip)
assert result.valid : result.errorCollection.errors

def updatedIssue = is.update(currentUser, result, EventDispatchOption.DO_NOT_DISPATCH, false).issue
Set Resolved date to Now Script
import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issueMutable = issue as MutableIssue
def issueManager = ComponentAccessor.issueManager
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

issueMutable.setResolutionDate(new Timestamp(System.currentTimeMillis()))

issueMutable.store()

 

Set Resolved date to Null Script

import java.sql.Timestamp
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issueMutable = issue as MutableIssue
def issueManager = ComponentAccessor.issueManager
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser

issueMutable.setResolutionDate(null)

issueMutable.store()
Thanks again for the help @Peter-Dave Sheehan 
Hopefully this is helpful to others seeking to escape the administrative tyranny of Resolution configurations.
Sarah Kladstrup
Contributor
January 13, 2023

Script Runner is giving me a warning about the issueMutable.store() line:

  • Use the Object's Service or Manager to save values. Since v5.0. DO NOT USE THIS as it overwrites all the fields of the issue which can result in difficult to reproduce bugs Prefer to use QueryDslAccessor to change only needed fields

Any suggestions?

Timothy Ryner May 22, 2023

I elected to use it anyway.  Not sure if it will have long-term implications, but so far so good.

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
VERSION
8.22.4
TAGS
AUG Leaders

Atlassian Community Events