Using Script Runner to update Custom Field A with a timestamp when Custom Field B is updated

David Vaughan April 14, 2023

My client wants to be able to create a filter for issues where Field A has been updated in the last 7 days. As Jira doesn't have the ability to filter for updates made to specific custom fields I am trying to set up another date custom field (Field B) and using a ScriptRunner Listener to add the date every time field A is updated.

I have used some of the logic provided in this post: https://community.atlassian.com/t5/Jira-questions/Scriptrunner-listener-to-update-custom-date-time-field/qaq-p/387444 

Specifically, the code below (although have changed the field names as needed). 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder

def cfm = ComponentAccessor.getCustomFieldManager()
def watch = cfm.getCustomFieldObjectByName("% Complete")
def watchValue = event.issue.getCustomFieldValue(watch)
def updatedWatch = cfm.getCustomFieldObjectByName("% Complete Updated")
def today = new java.sql.Timestamp(new Date().getTime())

if (event.getChangeLog().getRelated('ChildChangeItem').find{ it.field == '% Complete'}){

def changeHolder = new DefaultIssueChangeHolder()

updatedWatch.updateValue(null, event.issue, new ModifiedValue(null, today), changeHolder)

}
This seems to be working and whenever I update Field A, Field B is populated with the date. However, when trying to build a JQL search for "Field B" > -7d it is not returning results, and when I try "Field B" is not EMPTY I also get no results. However, if I search for the specific key of an issue with Field B populated I can see the date populated in the column. 

As a test, I manually updated Field B with tomorrows date which allows me to search for it in the issue navigator based on "Field B". I then updated Field A again which caused the Listener to update Field B with today;s date and viewing the issue in the issue navigator the date has been updated correctly to today's date. BUT, my JQL searches still seem to read it as tomorrows date, which I entered manually. 

The JQL search only seems to be able to interpret the data I have manually entered into the field, and disregards amendments made by the Listener. Have I missed something obvious in the Listener code or is it known behaviour that JQL cannot parse data entered onto a field by a ScriptRunner listener? 

1 answer

0 votes
Fazila Ashraf
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 14, 2023

Hi @David Vaughan 

Seems to be reindex related. 

Can you add the reindex action at the end of the script?

Found a reference in https://community.atlassian.com/t5/Jira-questions/Scriptrunner-reindex-an-issue/qaq-p/803412 but i haven't tested those snippets myself

David Vaughan April 14, 2023

Hi @Fazila Ashraf thank you for responding.

I have included some code found on the link you attached but unfortunately my listener is failing to execute correctly. The new code I am using is (changes in bold): 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils


def cfm = ComponentAccessor.getCustomFieldManager()
def watch = cfm.getCustomFieldObjectByName("% Complete")
def watchValue = event.issue.getCustomFieldValue(watch)
def updatedWatch = cfm.getCustomFieldObjectByName("% Complete Updated")
def today = new java.sql.Timestamp(new Date().getTime())

if (event.getChangeLog().getRelated('ChildChangeItem').find{ it.field == '% Complete'}){

def changeHolder = new DefaultIssueChangeHolder()

updatedWatch.updateValue(null, event.issue, new ModifiedValue(null, today), changeHolder)
}

def reIndexIssue(issue){
//Re-index the issue after update
boolean wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(issue)
ImportUtils.setIndexIssues(wasIndexing)



Have I arranged the code incorrectly? 
I receive an error at line 25 ( ComponentAccessor.getComponent(IssueIndexingService.class).reIndex(issue) ) that states: 

[Static type checking] - Cannot find matching method
com.atlassian.jira.issue.index.IssueIndexService#reIndex(java.lang.Object). 
Please check if the declared type is correct and if the method exists. 
Possible solutions: find() @ line 25, column 5.

Fazila Ashraf
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 14, 2023

can you try changing def reIndexIssue(issue){ to def reIndexIssue(event.issue){

David Vaughan April 14, 2023

I then get an "unexpected token" error at that line and when trying to save the listener get the below error:

The script could not be compiled:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script48.groovy: 21: unexpected token: ) @ line 21, column 29.
   def reIndexIssue(event.issue){
                               ^

1 error
Fazila Ashraf
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 14, 2023

Just noticed that you are not calling this function you defined, anywhere..

Can you use the below snippet in the bottom after removing the function definition part?

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

ImportUtils.setIndexIssues(isIndex);
David Vaughan April 17, 2023

Hi Fazila, I have removed the reIndexIssue function and replaced it with the MutableIssue script. Unfortunately, I now get the below error: 

The script could not be compiled:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script40.groovy: 21: unable to resolve class MutableIssue 
 @ line 21, column 14.
   MutableIssue mutableIssue = ComponentAccessor.getIssueManager().getIssueObject(event.issue.getKey());
                ^

1 error

.


script below:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils


def cfm = ComponentAccessor.getCustomFieldManager()
def watch = cfm.getCustomFieldObjectByName("% Complete")
def watchValue = event.issue.getCustomFieldValue(watch)
def updatedWatch = cfm.getCustomFieldObjectByName("% Complete Updated")
def today = new java.sql.Timestamp(new Date().getTime())

if (event.getChangeLog().getRelated('ChildChangeItem').find{ it.field == '% Complete'}){

def changeHolder = new DefaultIssueChangeHolder()

updatedWatch.updateValue(null, event.issue, new ModifiedValue(null, today), changeHolder)


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

ImportUtils.setIndexIssues(isIndex);

Fazila Ashraf
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 17, 2023

You will have to add these import lines as well

 

import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
David Vaughan April 17, 2023

I can now save the listener successfully, but it still fails to execute successfully: 

2023-04-17 11:23:29,296 ERROR [runner.AbstractScriptListener]: *************************************************************************************
2023-04-17 11:23:29,296 ERROR [runner.AbstractScriptListener]: Script function failed on event: com.atlassian.jira.event.issue.IssueEvent, file: null
java.lang.NullPointerException: Cannot invoke method updateValue() on null object
at Script59.run(Script59.groovy:20)


code:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue


def cfm = ComponentAccessor.getCustomFieldManager()
def watch = cfm.getCustomFieldObjectByName("% Complete")
def watchValue = event.issue.getCustomFieldValue(watch)
def updatedWatch = cfm.getCustomFieldObjectByName("% Complete Updated")
def today = new java.sql.Timestamp(new Date().getTime())

if (event.getChangeLog().getRelated('ChildChangeItem').find{ it.field == '% Complete'}){

def changeHolder = new DefaultIssueChangeHolder()

updatedWatch.updateValue(null, event.issue, new ModifiedValue(null, today), changeHolder)


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

ImportUtils.setIndexIssues(isIndex);
}

Suggest an answer

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

Atlassian Community Events