Groovy : Set Cascading Field Value

Hi everybody,

After looking for a solution myslef, I'm kind out of idea right now. Here's the situation :

Environment : Jira 5.2.11 / Script Runner 2.1.15

When creating an issue, (in post-function) I want to :

- Extract values from a cascading select field (Nature of the Request - cf id:17000)

- Check if the word 'QlikView' is in the first value

- Update another cascading field (System and Sub-System - cf id 11201) with values (existing in the field configuration, let's say Data - QlikView)

So far, I have this script I modified to fit my needs, BUT the updating process doesn't manage to update the cf 11201 value.

When checking the script with the script console, I don't have any error message and it gives me back the correct value (Data - QlikView).

But when I put the script in the workflow, the value isn't updated in the cf 11201

// and if I look at the logs, i have the following message :

// Caused by: java.lang.NoClassDefFoundError: com/opensymphony/user/User

// --> caused by a plugin old version

Do you have any clues about what's wrong?

Thanks!

import org.apache.log4j.Category
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem
import com.atlassian.jira.issue.customfields.manager.OptionsManager
import com.atlassian.jira.issue.customfields.view.CustomFieldParams
import com.atlassian.jira.issue.customfields.view.CustomFieldParamsImpl 
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutStorageException
import com.atlassian.jira.issue.customfields.impl.CascadingSelectCFType 
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.component.ComponentAccessor
import com.opensymphony.workflow.WorkflowContext;


MutableIssue issue = issue

CustomFieldManager cfManager2 = ComponentManager.getInstance().getCustomFieldManager()
// name of the first cascading field I will extract the value in order to check it
CustomField cf2 = cfManager2.getCustomFieldObject(17000)
String first = issue.getCustomFieldValue(cf2)?.values()*.value[0]
String second = issue.getCustomFieldValue(cf2)?.values()*.value[1]

// name of the cascading field where I want to put my new value
CustomField categoryCF = ComponentManager.getInstance().getCustomFieldManager().getCustomFieldObjectByName("Système et sous-système"); 
CustomFieldParams cfVal = issue.getCustomFieldValue(categoryCF) as CustomFieldParams

if (categoryCF == null || !(categoryCF.getCustomFieldType() instanceof CascadingSelectCFType)) {

log.error("Expected customfield doesn't exist or doesn't have the expected type.");

}
   
OptionsManager optionsManager = ComponentAccessor.getOptionsManager();

Option parentOptionObj = optionsManager.findByOptionId(11124); // value to put in first list     

Option childOptionObj = optionsManager.findByOptionId(11211);  // value to put in second list    

Map<String,Object> newValues = new HashMap<String, String>();

// checking if the value in the first cascading field is correct
if (first.contains('QlikView')) {

newValues.put(null, parentOptionObj); 
newValues.put("1", childOptionObj); 
// then updating the second cascading field with new values
categoryCF.updateValue(null, issue, new ModifiedValue(cfVal,newValues), new DefaultIssueChangeHolder());

issue.store()
}

11 answers

1 accepted

This widget could not be displayed.

Ok, if you want to update a blank cascading select field, you just have to put in comment the following lines :

// Object cValue = categoryCF.getValue(cIssue);

// if (cValue == null) {
// log.error("Expected customfield doesn't have any value.");
// }

and replace the last line of the script with :

categoryCF.updateValue(null, cIssue, new ModifiedValue(null, newValues), new DefaultIssueChangeHolder());

This widget could not be displayed.

Hi,

Using ComponentAccessor now

import com.atlassian.jira.component.ComponentAccessor
//(...)

CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
//(...)

def CUSTOM_FIELD_ID = 11201; // set your Cascading Select custom field Id

//Get your Cascading Select CF Object
def categoryCF = customFieldManager.getCustomFieldObject(CUSTOM_FIELD_ID);

 

If we want to find the Parent and the child Options Object from the string of their context label :

// if you know or you find these strings
String stParentOption = "Targeted parent option";
String stChildOption = "Targeted child option of the selected parent";

// you'll be able to find their Options objects as below
// null as parentId to find the string into the parent options list of the context
Option parentOptionObj = optionsManager.getOptions(categoryCF.getRelevantConfig(cIssue)).getOptionForValue(stParentOption,null)

// knowing the parent Option object, you'll find the children on its Id
Option childOptionObj = optionsManager.getOptions(myCascadingCf.getRelevantConfig(cIssue)).getOptionForValue(stChildOption,parentOptionObj.optionId)

and I add how I store it

def newValues = new HashMap();
newValues.put(null, parentOptionObj);
newValues.put("1", childOptionObj);

classificationObj.updateValue(null, cIssue, new ModifiedValue(null, newValues), changeHolder);

// deprecated way to store the issue 
cIssue.store()
// to be updated with something like
// issueManager.updateIssue(cUser, cIssue, EventDispatchOption.DO_NOT_DISPATCH, false);

 

have fun smile

This widget could not be displayed.

you have to use following import

import com.atlassian.crowd.embedded.api.User;

opensymphony user has deprecated!!

This widget could not be displayed.

The above script appear to be correct for me.

You check any validator script running in the transition which uses com/opensymphony/user/User object.

it should be import com.atlassian.crowd.embedded.api.User; as prasad suggested

This widget could not be displayed.

Hi, thanks to both of you.

The opensymphony error was generated by one plugin (condition validator) which was in an old version (because I don't use it, I've dactivated it) BUT, my script still doesn't update the cascading select field.

yes in the workflow transition you check any other validators or postfunctions present.

Eventhough you disabled, those were not removed from the transition. You need to manually remove them.

This widget could not be displayed.

Thanks Bharadwaj, I followed your advice, but still having troubles to make my script working.

In the logs, I have now a NullPointerException, which I don't get because it works in the Script Console.

Any ideas?

This widget could not be displayed.

check the "setFieldValue" method on following class, i hope it will help you

https://github.com/atlassian/jira-suite-utilities/blob/master/src/main/java/com/googlecode/jsu/util/WorkflowUtils.java

Thanks Rambanam, i'll check and let you know! :)

Cheers!

This widget could not be displayed.

Ok, I made some progress. Right now, this script can modify the value of a cascading select field already containing values. I have now to make it work with an empty field.

Hope this first script can help some people :

import org.apache.log4j.Category
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem
import com.atlassian.jira.issue.customfields.manager.OptionsManager
import com.atlassian.jira.issue.customfields.view.CustomFieldParams
import com.atlassian.jira.issue.customfields.view.CustomFieldParamsImpl 
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutStorageException
import com.atlassian.jira.issue.customfields.impl.CascadingSelectCFType 
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.crowd.embedded.api.User;


MutableIssue cIssue = issue

// get customfield
CustomField categoryCF = ComponentManager.getInstance().getCustomFieldManager().getCustomFieldObject(11201);
 
if (categoryCF == null || !(categoryCF.getCustomFieldType() instanceof CascadingSelectCFType)) {
log.error("Expected customfield doesn't exist or doesn't have the expected type.");
}
 
OptionsManager optionsManager = (OptionsManager)ComponentManager.getComponentInstanceOfType(OptionsManager.class);
// get customfield value
Object cValue = categoryCF.getValue(cIssue);

if (cValue == null) {
log.error("Expected customfield doesn't have any value.");
}

Option parentOptionObj = optionsManager.findByOptionId(11124); // get first value to input    

Option childOptionObj = optionsManager.findByOptionId(11211);  // get second value to input     

Map<String,Object> newValues = new HashMap<String, String>();

newValues.put(null, parentOptionObj); //input value in first dropdown

newValues.put("1", childOptionObj); // input value in second dropdown

//update issue and save modifications in database
categoryCF.updateValue(null, cIssue, new ModifiedValue(cValue, newValues), new DefaultIssueChangeHolder()); 
issue.store()

This widget could not be displayed.

Rambanam, because you proposed some solutions for the previous problems, I give you the karma points.

Thanks again! (thanks also to Bharadwaj!)

This widget could not be displayed.

A script below I used to copy values from a source Cascading Select CF, named "Classification Incident", to a target one, named "Classification", using a JQL query filter

/**
 * Created by robert_mota on 18/12/2014.
 * Script to use one shot to copy "Classification Incident" CF
 * into "Classification"
 *
 * Prerequisite : all the values of the source Cascading Select CF 
 * must be in context configuration of the target CF
 */
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.customfields.manager.OptionsManager
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.web.bean.PagerFilter
import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.issue.ModifiedValue

CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
IssueChangeHolder changeHolder = new DefaultIssueChangeHolder();

Logger log = Logger.getLogger("com.onresolve.jira.groovy")
log.setLevel(Level.DEBUG)

log.debug("Begin: Fix Classification ****************************");

OptionsManager optionsManager = ComponentAccessor.getOptionsManager();

// to test on 1 issue
//jqlSearch = "project = CSI AND issuetype = incident AND \"Classification Incident\" is not EMPTY and Classification is not EMPTY and key in (CSI-23015)"
// to run on all issues of the search
jqlSearch = "project = CSI AND issuetype = incident AND \"Classification Incident\" is not EMPTY and Classification is not EMPTY"

SearchService searchService = ComponentAccessor.getComponent(SearchService.class)

// ComponentAccessor.getJiraAuthenticationContext().getUser() returns an ApplicationUser
// To get the User needed for SearchService below, use getDirectoryUser() method
User user = ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser()

IssueManager issueManager = ComponentAccessor.getIssueManager()

if (!user) {
    log.debug("User not found ****************************");
    return false;
}

SearchService.ParseResult parseResult =  searchService.parseQuery(user, jqlSearch)
if (parseResult.isValid()) {
    def searchResult = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
    // Transform issues from DocumentIssueImpl to the "pure" form IssueImpl (some methods don't work with DocumentIssueImps)
    def issues = searchResult.issues.collect {issueManager.getIssueObject(it.id)}
    issues.each {
        MutableIssue incident = it as MutableIssue;

        // Source CF named "Classification Incident" id = 10490
        def classificationIncidentObj = customFieldManager.getCustomFieldObjectByName("Classification Incident");
        // to use ID
        //def classificationIncidentObj = customFieldManager.getCustomFieldObject(10490);

        // Target CF cible Classification id = 10367
        def classificationObj = customFieldManager.getCustomFieldObjectByName("Classification");
        //def classificationObj = customFieldManager.getCustomFieldObject(10367);

        def classificationIncident = classificationIncidentObj.getValue(incident);
        //log.debug ("\n      Incident: Classification Incident Parent: " + classificationIncident.get(null));
        //log.debug ("\n      Incident: Classification Incident Child: " + classificationIncident.get("1"));

        String stParentOption = classificationIncident.get(null);
        String stChildOption = classificationIncident.get("1");
        //log.debug ("\n      Incident: stParentOption = " + stParentOption);
        //log.debug ("\n      Incident: stChildOption = " + stChildOption);

        Option parentOptionObj = optionsManager.getOptions(classificationObj.getRelevantConfig(incident)).getOptionForValue(stParentOption,null)
        //log.debug ("\n      Target Parent Classification = " + parentOptionObj);

        Option childOptionObj = optionsManager.getOptions(classificationObj.getRelevantConfig(incident)).getOptionForValue(stChildOption,parentOptionObj.optionId)
        //log.debug ("\n      Target Classification child ("+ parentOptionObj + ") = " + childOptionObj);

        def newValues = new HashMap();
        newValues.put(null, parentOptionObj);
        newValues.put("1", childOptionObj);

        //log.debug ("\n      Target Classification Parent new value: " + newValues.get(null));
        //log.debug ("\n      Target Classification Child new value: " + newValues.get("1"));

        // To store it
        classificationObj.updateValue(null, incident, new ModifiedValue(null, newValues), changeHolder);

        // deprecated method to store issues
        //incident.store()

        // new way to store issues
        issueManager.updateIssue(user, incident, EventDispatchOption.DO_NOT_DISPATCH, false);
    }
} else {
    log.debug("Invalid JQL: " + jqlSearch);
    return false;
}
log.debug("End: Fix Classification ****************************");
return true;

Regards

This widget could not be displayed.

Which of the scripts above could be used to copy the contents of a select list (single) to a select list (cascading), changing from a single entry to a map form?

Suggest an answer

Log in or Sign up to answer
Atlassian Summit 2018

Meet the community IRL

Atlassian Summit is an excellent opportunity for in-person support, training, and networking.

Learn more
Community showcase
Posted Wednesday in New to Jira

Are you planning to trial, or are currently trialling Jira Software? - We want to talk to you!

Hello! I'm Rayen, a product manager at Atlassian. My team and I are working hard to improve the trial experience for Jira Software Cloud. We are interested in   talking to 20 people planning t...

128 views 2 0
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