Update custom field when scheduler runs groovy script

Robi Anton April 13, 2016

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

2 votes
Thanos Batagiannis _Adaptavist_
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.
April 13, 2016

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);
}
Robi Anton April 14, 2016

@Thanos Batagiannis [Adaptavist]

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

rTrack Support May 13, 2016

Hi robi anton,

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

can u help on this .

1 vote
Jozef Kotlár
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.
April 14, 2016

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()
0 votes
Robi Anton April 13, 2016

@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
Thanos Batagiannis _Adaptavist_
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.
April 13, 2016

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

Robi Anton April 13, 2016

@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] 

 

Thanos Batagiannis _Adaptavist_
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.
April 14, 2016

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.

Robi Anton April 17, 2016

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"

 

 

Robi Anton April 19, 2016

@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!! 

JamieA
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.
April 19, 2016

issue.estimate

Robi Anton April 20, 2016

@Jamie Echlin [Adaptavist]

Could you give me the full line please? 

JamieA
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.
April 20, 2016

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

Robi Anton April 20, 2016

@Jamie Echlin [Adaptavist]

 

But how to use it on my script? 

 

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

JamieA
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.
April 20, 2016

try issue.originalEstimate

Robi Anton April 20, 2016

@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
}
0 votes
Aleks Yenin (Polontech)
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.
April 13, 2016

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
Robi Anton April 13, 2016

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

 

rTrack Support May 13, 2016

Hi robi,

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

Suggest an answer

Log in or Sign up to answer