Update custom field when scheduler runs groovy script

Hello, 

Usually, we run groovy script on workflow's post function. 

On my case, I am calling a groovy script each day from a scheduler (Systems-> Services)

On this script I have to update a value of a custom field.

I am able to update a value from a post function, but how to do in this case?

 

Thanks smile

 

4 answers

Hi Robi,

This depends on the type of your custom field, but for a test field should be something like 

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

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def tgtField = customFieldManager.getCustomFieldObjects(issue).find {it.name == "testString"}
if (tgtField) {
	def changeHolder = new DefaultIssueChangeHolder();
	tgtField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(tgtField), "new value"),changeHolder);
}

@Thanos Batagiannis [Adaptavist]

I don't want to call my script from my workflow, but from scheduler...

Hi robi anton,

how u create a new custom field in JIRA with groovy script runner .

can u help on this .

When your script is run from scheduler, there is no proper authentication context (no one is logged in), so you need to replace your code

def user = ComponentAccessor.getJiraAuthenticationContext().getUser().directoryUser

with

def USER_NAME = "jira-automation" // replace with existing user
def user = ComponentAccessor.getUserManager().getUserByName(USER_NAME)?.getDirectoryUser()

Here is script for updating one cf depending on another :

import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.customfields.option.Options;
import com.atlassian.jira.issue.customfields.option.Option;
import com.atlassian.jira.issue.ModifiedValue;
import com.atlassian.jira.component.ComponentAccessor;

// Custom field ID's
int sourceCfID = 10101;
int targetCfID = 10102;

// Option ID's of target custom field
int optionFirst = 99998;
int optionSecond = 99999;

CustomFieldManager customFieldManager = ComponentManager.getInstance().getCustomFieldManager();

String sourceCfValue = customFieldManager.getCustomFieldObject(sourceCfID).getValue(issue).toString();

CustomField targetCf = customFieldManager.getCustomFieldObject(targetCfID);

// If value of source custom field is XXX, then select first option, if NOT select second option
// This can be replaced by switch statement if you need to check more options!!!!
int targetCfOption = (sourceCfValue == 'XXX') ? optionFirst : optionSecond;

Options targetCfOptions = ComponentAccessor.getOptionsManager().getOptions(targetCf.getRelevantConfig(issue));

Option ChoosenOption = targetCfOptions.getOptionById(targetCfOption);

targetCf.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValu

Why source/target??? And I dont have option son my custom field

 

Hi robi,

are you updating the custom field values for multi level or single level ?

@Thanos Batagiannis [Adaptavist]

 

This is my script that I call from my wf postfunction. 

 

When I call it from the scheduler, it fails for the variable user, also for the jquery, etc. 

Could someone correct my script ?

The script should be ran by scheduler . Thanks

 

import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.web.bean.PagerFilter
 
def jqlSearch = "project = 'AISC - Suivi des demandes' and ('Date de livraison' >= startOfMonth(-1) and 'Date de livraison' <=endOfMonth(-1))"
//If you are going to use a JIRA version => 7 then you should get the ApplicationUser instead of User, to do that remove the .directoryUser
def user = ComponentAccessor.getJiraAuthenticationContext().getUser().directoryUser
def searchService = ComponentAccessor.getComponentOfType(SearchService.class)
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)
 
def value = 0
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def myCustomField = customFieldManager.getCustomFieldObjectByName("Temps d'intervention")
 
if (parseResult.isValid()) {
try {
def results = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
def issues = results.getIssues()
issues.each {
if (it.getCustomFieldValue(myCustomField))
value += it.getCustomFieldValue(myCustomField).toInteger()
 
}
} catch (SearchException e) {
e.printStackTrace()
}
} else {
log.warn("Invalid query")
return null
}
 
return value

JIRA version ? Also how this script connects to the 'update custom field' question ? Also more information (maybe a screenshot) on the service configuration and any errors in your log files... Help me to help you smile

@Thanos Batagiannis [Adaptavist]

My JIRA version is 6.2.6

Script Runner Version: 2.1.16

Maybe my question was not so clear. I will explain properly smile

I want to use the same groovy script that I was using from my workflow (on a transition).
As I want to run it all the first of month, I need to call this script from a scheduler.

What does my script do?

It executes a jqlquery, then for each issue, it calculates a sum of values contained on a custom value.

 Then I update the calculated value on an other custom field.

From a workflow post-function, It works properly, but when I call it form a scheduler, I have some ERRORS. 

 

When I am running the script by the scheduler:

 

2016-04-14 10:07:06,171 QuartzScheduler_Worker-3 ERROR ServiceRunner Statistiques [onresolve.jira.gr oovy.GroovyRunner] The script failed : javax.script.ScriptException: javax.script.ScriptException: java .lang.NullPointerException: Cannot get property 'directoryUser' on null object
2016-04-14 10:07:06,171 QuartzScheduler_Worker-3 ERROR ServiceRunner Statistiques [onresolve.jira.gr oovy.GroovyService] Script service failed: /app/jira/jira_data/scripts/scriptSommeCourant.groovy
javax.script.ScriptException: javax.script.ScriptException: java.lang.NullPointerException: Cannot get property 'directoryUser' on null object
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:117)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:103)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:212)
at com.onresolve.jira.groovy.GroovyRunner.runFile(GroovyRunner.java:102)
at com.onresolve.jira.groovy.GroovyRunner.run(GroovyRunner.java:62)
at com.onresolve.jira.groovy.GroovyService.run(GroovyService.java:52)
at com.atlassian.jira.service.JiraServiceContainerImpl.run(JiraServiceContainerImpl.java:61)
at com.atlassian.jira.service.ServiceRunner.execute(ServiceRunner.java:48)
at org.quartz.core.JobRunShell.run(JobRunShell.java:195)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)
Caused by: javax.script.ScriptException: java.lang.NullPointerException: Cannot get property 'directory User' on null object
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:318)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:111)
... 9 more
Caused by: java.lang.NullPointerException: Cannot get property 'directoryUser' on null object
at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:56)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:156)
at org.codehaus.groovy.runtime.callsite.NullCallSite.getProperty(NullCallSite.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java: 227)
at Script97.run(Script97.groovy:27)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:315)
... 10 more

 

For the error of directoryUser, the script search a current user executing the script. But there is no user running this..only the scheduler. I have tried to give the id of variable "user" manually like:

def user = "yk3520:10100"

 

Second error:

2016-04-14 10:31:06,053 QuartzScheduler_Worker-2 ERROR ServiceRunner Statistiques [onresolve.jira.groovy.GroovyRunner] The script failed : javax.script.ScriptException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.bc.issue.search.DefaultSearchService.parseQuery() is applicable for argument types: (java.lang.String, java.lang.String) values: [yk3520:10100, project = 'AISC - Suivi des demandes' and ('Date de livraison' >= startOfMonth() and 'Date de livraison' <=now()) and status = Validation]
Possible solutions: parseQuery(com.atlassian.crowd.embedded.api.User, java.lang.String)
2016-04-14 10:31:06,055 QuartzScheduler_Worker-2 ERROR ServiceRunner Statistiques [onresolve.jira.groovy.GroovyService] Script service failed: /app/jira/jira_data/scripts/scriptSommeCourant.groovy
javax.script.ScriptException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.bc.issue.search.DefaultSearchService.parseQuery() is applicable for argument types: (java.lang.String, java.lang.String) values: [yk3520:10100, project = 'AISC - Suivi des demandes' and ('Date de livraison' >= startOfMonth() and 'Date de livraison' <=now()) and status = Validation]
Possible solutions: parseQuery(com.atlassian.crowd.embedded.api.User, java.lang.String)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:117)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:103)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:212)
at com.onresolve.jira.groovy.GroovyRunner.runFile(GroovyRunner.java:102)
at com.onresolve.jira.groovy.GroovyRunner.run(GroovyRunner.java:62)
at com.onresolve.jira.groovy.GroovyService.run(GroovyService.java:52)
at com.atlassian.jira.service.JiraServiceContainerImpl.run(JiraServiceContainerImpl.java:61)
at com.atlassian.jira.service.ServiceRunner.execute(ServiceRunner.java:48)
at org.quartz.core.JobRunShell.run(JobRunShell.java:195)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)
Caused by: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.bc.issue.search.DefaultSearchService.parseQuery() is applicable for argument types: (java.lang.String, java.lang.String) values: [yk3520:10100, project = 'AISC - Suivi des demandes' and ('Date de livraison' >= startOfMonth() and 'Date de livraison' <=now()) and status = Validation]
Possible solutions: parseQuery(com.atlassian.crowd.embedded.api.User, java.lang.String)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:318)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:111)
... 9 more
Caused by: groovy.lang.MissingMethodException: No signature of method: com.atlassian.jira.bc.issue.search.DefaultSearchService.parseQuery() is applicable for argument types: (java.lang.String, java.lang.String) values: [yk3520:10100, project = 'AISC - Suivi des demandes' and ('Date de livraison' >= startOfMonth() and 'Date de livraison' <=now()) and status = Validation]
Possible solutions: parseQuery(com.atlassian.crowd.embedded.api.User, java.lang.String)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55)
at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at Script96.run(Script96.groovy:30)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:315)
... 10 more

 

 

For resume: 

I want to get a sum of values from custom fields of issues. 
The value of this sum should be updated on a another custom field.
 And this script should be ran by a scheduler. 

Hope you can help me now smile @Thanos Batagiannis [Adaptavist] 

 

Robi,

What Jozef spotted plus the latest part where you should get the custom field of the issue that you want to save the result.

import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.web.bean.PagerFilter


def jqlSearch = "project = 'AISC - Suivi des demandes' and ('Date de livraison' &gt;= startOfMonth(-1) and 'Date de livraison' &lt;=endOfMonth(-1))"
def userManager = ComponentAccessor.getUserManager()
def issueManager = ComponentAccessor.getIssueManager()
def searchService = ComponentAccessor.getComponentOfType(SearchService)

def user = userManager.getUserByName("admin").directoryUser
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)

def value = 0
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def myCustomField = customFieldManager.getCustomFieldObjectByName("Number Field")

if (parseResult.isValid()) {
    try {
        def results = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
        results.getIssues().each { value += it.getCustomFieldValue(myCustomField) ?: 0 }
    } catch (SearchException e) {
        e.printStackTrace()
    }
} else {
    log.warn("Invalid query")
    return null
}

// Save the value at custom field with name A number custom field of issue with key TEST-1
def targetIssue = issueManager.getIssueByCurrentKey("TEST-1")
def customField = customFieldManager.getCustomFieldObjects(targetIssue).find {it.name == "A number custom field"}
if (customField) {
    def changeHolder = new DefaultIssueChangeHolder()
    customField.updateValue(null, targetIssue,
            new ModifiedValue(targetIssue.getCustomFieldValue(customField), value),changeHolder)
    log.debug "Custom field $customField.name} updated with value ${value}"
    return
}

log.warn "Custom field ${customField?.name} was not updated"

Maybe there should be a couple of checks that you should take care (such as if the jql returned any issues) in order to know exactly what went wrong. But the script above is a tested and is a good start.

Thanks a lot @Thanos Batagiannis [Adaptavist]

 

It works very well. 

As you said, I have to make a couple of checks to run the script. 
I had to replace the first if condition my my own script. 

Then in the line "new ModifiedValue(targetIssue.getCustomFieldValue(customField), value),changeHolder)", I have to add value.toString(). 

 

Here is my script:

 

import org.apache.log4j.Category
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchException
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.customfields.manager.OptionsManager
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.*;
import java.sql.Timestamp
Calendar localCalendar = Calendar.getInstance(TimeZone.getDefault());
int currentDay = localCalendar.get(Calendar.DATE);
println currentDay


def jqlSearch = "project = 'AISC - Suivi des demandes' and status was in (Validation) and ('Date de livraison' &gt;= startOfMonth(-1) and 'Date de livraison' &lt;=endOfMonth(-1))"
def userManager = ComponentAccessor.getUserManager()
def issueManager = ComponentAccessor.getIssueManager()
def searchService = ComponentAccessor.getComponentOfType(SearchService)
def user = userManager.getUserByName("yk3520").directoryUser
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)

def value = 0
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def myCustomField = customFieldManager.getCustomFieldObjectByName("Temps d'intervention")

if (parseResult.isValid()) {
try {
def results = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
def issues = results.getIssues()
issues.each {
if (it.getCustomFieldValue(myCustomField))
value += it.getCustomFieldValue(myCustomField).toInteger()
}
} catch (SearchException e) {
e.printStackTrace()
}
} else {
log.warn("Invalid query")
return null
}

// Save the value at custom field with name A number custom field of issue with key TEST-1
def targetIssue = issueManager.getIssueByCurrentKey("SL-8")
def customField = customFieldManager.getCustomFieldObjects(targetIssue).find {it.name == "Total intervention M-1"}
if (customField) {
    def changeHolder = new DefaultIssueChangeHolder()
    customField.updateValue(null, targetIssue,
            new ModifiedValue(targetIssue.getCustomFieldValue(customField), value.toString()),changeHolder)
    log.debug "Custom field $customField.name} updated with value ${value}"
    return
}
log.warn "Custom field ${customField?.name} was not updated"

 

 

@Thanos Batagiannis [Adaptavist] 

@Jamie Echlin [Adaptavist]

Hi, 

 

In the above script, I was able to get the value of custom fields. But now, I want to get the values of system fields like "Orignal estimate". 

I have tried to change this line :

def myCustomField = customFieldManager.getCustomFieldObjectByName("Temps d'intervention")

by 

def myCustomField = customFieldManager.getEstimate()

But I have error like: 

No signature of method: com.atlassian.jira.issue.managers.DefaultCustomFieldManager.getEstimate() is applicable for

Am I correct? 
Or is there other way to do that?

 

Thanks!! 

issue.estimate

@Jamie Echlin [Adaptavist]

Could you give me the full line please? 

that was the full line, to get the issue estimate. It's not a custom field, so don't use the custom field manager. 

@Jamie Echlin [Adaptavist]

 

But how to use it on my script? 

 

It returns a value who is not the original estimated time.

try issue.originalEstimate

@Jamie Echlin [Adaptavist]

Thanks a lot, it works! smile

Here is my correction :

 

if (parseResult.isValid()) {
try {
def results = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
def issues = results.getIssues()
issues.each {
if (it.originalEstimate)
//60 beacause it.originalEstimate returns the value in seconds
value += (it.originalEstimate)/60
}
} catch (SearchException e) {
e.printStackTrace()
}
} else {
log.warn("Invalid query")
return null
}

Suggest an answer

Log in or Join to answer
Community showcase
Sarah Schuster
Posted Jan 29, 2018 in Jira

What are common themes you've seen across successful & failed Jira Software implementations?

Hey everyone! My name is Sarah Schuster, and I'm a Customer Success Manager in Atlassian specializing in Jira Software Cloud. Over the next few weeks I will be posting discussion topics (8 total) to ...

3,321 views 14 20
Join discussion

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
Atlassian Team Tour

Join us on the Team Tour

We're bringing product updates and pro tips on teamwork to ten cities around the world.

Save your spot