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.