JIRA Cascading Select in Jython
The Cascading Select Custom Field type in JIRA is a bear. The first trick is learning to set the “null” value and then the “1” child value. The next trick is building out a ModifiedValue object to hold your change. Then you get to jump down the rabbit hole of finding the correct Option values for the custom field, and setting them with the tricks just mentioned.
So, in the interests of saving me sanity next time I need to set a Cascading Select, here’s a Jython function that works in Jira 4.2:
import logging from com.atlassian.jira import ComponentManager from com.atlassian.jira.issue.customfields.manager import OptionsManager from com.atlassian.jira.issue.customfields.view import CustomFieldParamsImpl from com.atlassian.jira.issue import ModifiedValue from com.atlassian.jira.issue.util import DefaultIssueChangeHolder from java.util import HashSet # cf = custom field # issue = issue to modify # parent = top value to set (string value) # child = child value to set (string value) def set_cascading_select(cf, issue, parent, child): # Get the managers cfm = ComponentManager.getInstance().getCustomFieldManager() om = ComponentManager.getComponentInstanceOfType(OptionsManager) fli = ComponentManager.getInstance().getFieldLayoutManager().getFieldLayout(issue).getFieldLayoutItem(cf) parent_options = om.getOptions(cf.getRelevantConfig(issue)) parent_option = None child_option = None try: parent_option = parent_options.getOptionForValue(parent, None) except: pass try: child_option = parent_options.getOptionForValue(child, parent_option.getOptionId()) except: pass if parent_option and child_option: old_application = issue.getCustomFieldValue(cf) new_application = CustomFieldParamsImpl(cf) a_none = HashSet() a_none.add(parent_option) a_1 = HashSet() a_1.add(child_option) new_application.put(None, a_none) new_application.put("1", a_1) mf = ModifiedValue(old_application, new_application) cf.updateValue(fli, issue, mf, DefaultIssueChangeHolder()) logging.debug("set issue " + issue.getKey() + " cf " + cf.getName() + " setting " + parent + "/" + child) return True else: logging.error("invalid parent/child option: " + parent + "/" + child) return None |
Example function calls from within a validation hook:
cfm = ComponentManager.getInstance().getCustomFieldManager() application_cf = cfm.getCustomFieldObjectByName("Beverages") # good set_cascading_select(application_cf, issue, "Hard Drinks", "Whiskey") # bad child set_cascading_select(application_cf, issue, "Hard Drinks", "Coke") # bad parent set_cascading_select(application_cf, issue, "Soft Drinks", "Whiskey") # total crap set_cascading_select(application_cf, issue, "Illicit Drugs", "Bath Salts") |
The logging
stuff is useful for debugging, if you have that set up, else just remove those bits.