Hey folks,
I'm trying to compare the values returned from a multi-select field on an issue to the key in a map. From the map, I'm attempting to then use the related map value to get the applicable context of a field and set values on a separate field as a behavior.
I'm running into issues when iterating through the values returned by the multi-select. Its printing the function correctly, but if I add an `if` to attempt to compare to the map.key it doesn't seem to run through that- I believe this is possible in groovy, but happy to be redirected!
def im = ComponentAccessor.issueManager
def issue = im.getIssueObject('AWOLBP-2') //in a workflow postfunction, you can just comment out this line, the script binding already includes an issue variable
log.warn "Using $issue"
def cfManager = ComponentAccessor.customFieldManager
def optionsManager = ComponentAccessor.optionsManager
// Field and Project Value Map
def projectContextMap = [
'Foo':16601, // for dev testing. comment out in prod.
'Bar':16901 // for dev testing. comment out in prod.
]
def projectContextMapKey = projectContextMap.key
def projectContextMapId = projectContextMap.value
for (e in projectContextMap){ // checking format of the full map
log.warn "key = ${e.key}, Value = ${e.value}"
}
// Field Configuration Area
def productGroup = cfManager.getCustomFieldObjectsByName('Group')
def groupValue = productGroup ? issue.getCustomFieldValue(productGroup[0]) : "Field doesn't exist"
log.warn "Custom field value type or class" + groupValue.getClass()
groupValue.eachWithIndex { it, i ->
log.warn "Group Values $i: $it"
}
def productTeam = getFieldById("customfield_19403") // Product Team(s) Jira Test ID 19403
def productTeamObjects = cfManager.customFieldObjects.findByName("Product Team(s)")
log.warn("ProductTeam Objects: $productTeamObjects")
// Iterate through the Group value list and add context values
for (i in groupValue){
log.warn "Present Value for $issue: $i"
if (projectContextMap.containsKey(i)) {
return true //I expect this part to be returning something if the value is `Foo`
log.warn "$i matched with ${projectContextMap.value}"
}
}
2023-03-02 15:13:12,903 WARN [runner.ScriptBindingsManager]: Custom field value type or classclass java.util.ArrayList 2023-03-02 15:13:12,904 WARN [runner.ScriptBindingsManager]: Group Values 0: Data Security 2023-03-02 15:13:12,904 WARN [runner.ScriptBindingsManager]: Group Values 1: ADEMO 2023-03-02 15:13:12,906 WARN [runner.ScriptBindingsManager]: ProductTeam Objects: Product Team(s) 2023-03-02 15:13:12,906 WARN [runner.ScriptBindingsManager]: Present Value for AWOLBP-2: Bar 2023-03-02 15:13:12,906 WARN [runner.ScriptBindingsManager]: Present Value for AWOLBP-2: Foo
Appreciate any direction!
I have a few questions before I can understand what you're trying to do:
But after writing all this, maybe I have an inkling of what might be going on.
Are you using your AWOLBP-2 issue as a sort of external variable and all new tickets will lookup details in that issue?
I think the main crux of your issue is that you need to realize that issue.getCustomFieldValue(cf) for cf of type Single Select, Multi Select and Checkbox, the returned value will be either an "Option" object or an ArrayList of Options. So if you want to compare that against a Map key or lookup values in another context, you need to extract the value of the option with "getValue()".
But also, I realized at the end that you "return true" was just before your last log.warn. So the warning would never be printed to the log. So maybe this was working all along, but you couldn't see it.
Here is some groovy-fication of your code:
import com.atlassian.jira.issue.customfields.option.Option
def im = ComponentAccessor.issueManager
def issue = im.getIssueObject('AWOLBP-2') //in a workflow postfunction, you can just comment out this line, the script binding already includes an issue variable
log.warn "Using $issue"
def cfManager = ComponentAccessor.customFieldManager
def optionsManager = ComponentAccessor.optionsManager
// Field and Project Value Map
def projectContextMap = [
'Foo': 16601, // for dev testing. comment out in prod.
'Bar': 16901 // for dev testing. comment out in prod.
]
def projectContextMapKey = projectContextMap.key
def projectContextMapId = projectContextMap.value
//in groovy, the collection methods are so much easier than for-loop constructs
//for maps, you can use either 1 or 2 arguments in your closure.
//If you use 1, it will be a map item and you can access key and value separately
//but I like to use 2 and name my key and value according to what they represent
//here is my guess for what you mean (simply 'k,v ->' would also work):
projectContextMap.each{projectKey, projectId ->
log.warn "Key = $projectKey, Value = $projectId"
}
// Field Configuration Area
def productGroupCfs = cfManager.getCustomFieldObjectsByName('Group')
assert productGroupCfs: "No custom field found matching name: 'Group'" //you probably want to abort early if the custom field can't be found
def groupValues = issue.getCustomFieldValue(productGroupCfs[0]) as List<Option> //here I tell the syntax checker what to expect so .value below won't raise a static type checking errors
log.warn "Custom field value type or class" + groupValues.getClass()
groupValues.eachWithIndex { it, i ->
log.warn "Group Values $i: $it.value" //explicitly using the .value rather than letting the string coercion convert it
}
def productTeam = getFieldById("customfield_19403") // Product Team(s) Jira Test ID 19403
def productTeamObjects = cfManager.customFieldObjects.findByName("Product Team(s)")
log.warn("ProductTeam Objects: $productTeamObjects")
// Iterate through the Group value list and add context values
groupValues.each{ groupOption->
log.warn "Present Value for $issue: $groupOption.value"
if (projectContextMap.containsKey(groupOption.value)) {
log.warn "$groupOption.value ($groupOption.optionId) matched with ${projectContextMap.value}"
return true //I expect this part to be returning something if the value is `Foo`
}
}
Hey Peter,
Thanks for taking a look. Here are answers to some of your questions:
Correct, ideally during creation and editing the issue.
That's for testing purposes only, I would definitely remove this when I have implemented the behavior. Sorry for the confusion.
Multi-Select
You're right- no errors, I meant output.
I'll circle back and test this, if it really was working then I'll be very happily surprised :D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
With a few tiny edits this is working as expected and will stack any values returned by the multi-select. Thank you for your expertise, Peter!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The problem I see here is that Map contains multiple key:value pairs, so trying to match or look into the map you must iterate over either the keys, or values. Things like map.key is not valid and wouldn't work with Java. It's possible that groovy allows you to do this if the map only contains a single k:v pair, because groovy is.. well, groovy. It does groovy things. Or maybe it just takes the first element of the map. I'm not sure, would need to check groovy docs, but we can conclude that just .key or .value won't work on maps.
Strictly java speaking, some examples
Map<String, Integer> map = new HashMap<>();
map.put("Foo", 111);
map.put("Bar", 222);
// ------------ Splitting the map into key:value collections
//If I want to collect KEYS
Set<String> mapKeys = map.keySet();
//If I want to collect VALUES
Collection<Integer> values = map.values();
// ------------ Checking if either collection contains what I'm looking for
//If I want to look into mapKeys, or values
if (mapKeys.contains("Bar")) {
//do something
}
if (values.contains(222)) {
// do something
}
// ------------ Back to the original map
//If I want to check if the map contains a key
if (map.containsKey("Bar")) {
// do something
}
//If I want to check if the map contains a value
if (map.containsValue(222)) {
// do something
}
//If I want to find "Bar" in the original map, the long variant
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getKey() == "Bar") {
//We found Bar!
Integer barValue = entry.getValue(); // 222
}
}
//If I want to find "Bar" in the original map, the short variant
Integer barValue = map.get("Bar"); //Either 222, or null
So with groovy you can of course just use "def" - groovy will know what types those are, but this is just to show the actual types you're working with. Hopefully one of those examples helps.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks Radek, I have this working if I explicitly hard code the options- however I'm trying to essentially look up if the option is applicable in a field context to avoid the long-term maintenance.
So, if the `Group` field contains `Bar`, I would look to the applicable contexts of the `Product Team(s)` field and return those values.
With those values, I would add them to a final list.
If there were multiple matches in the `Group` multi-select, I would return those values and add them to the final list as well.
So, if `Group` == [Foo,Bar] I would look up and return the values for Foo. Then I would look up and return the values for Bar.
Both of those would create the final list to be displayed as options in the `Product Team(s)` field.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.