Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

In a Behavior/Groovy, Comparing an i value to a map

Andrew Wolpers March 2, 2023

Hey folks,

Overview

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.

The Problem

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!

The script I'm using:



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}"
}
}

The error:

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!

2 answers

1 accepted

Suggest an answer

Log in or Sign up to answer
0 votes
Answer accepted
Peter-Dave Sheehan
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.
March 2, 2023

I have a few questions before I can understand what you're trying to do:

  1. This behaviour, is it targeted for an issue creation? I.e. during creation of the issue, user fills in the multiselect and based on the selected value(s), you want to adjust/set the value on the "Product Team(s)" field?
  2. Or will this need to run at some later point when the user open the issue for editing?
    The reason I ask is because of the hardcoded "issue" variable. In a behaviour, you usually work with either issueContext (during creation) or underlyingIssue (during edits).
  3. What type of field is "Product Team(s)"?
  4. What is the error you are getting and on which line? What I see is just the normal output from your script?

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`
}
}

 

Andrew Wolpers March 6, 2023

Hey Peter,

Thanks for taking a look. Here are answers to some of your questions:

  1. This behaviour, is it targeted for an issue creation? I.e. during creation of the issue, user fills in the multiselect and based on the selected value(s), you want to adjust/set the value on the "Product Team(s)" field?

Correct, ideally during creation and editing the issue.

  1. Or will this need to run at some later point when the user open the issue for editing?
    The reason I ask is because of the hardcoded "issue" variable. In a behaviour, you usually work with either issueContext (during creation) or underlyingIssue (during edits).

That's for testing purposes only, I would definitely remove this when I have implemented the behavior. Sorry for the confusion.

  1. What type of field is "Product Team(s)"?

Multi-Select

  1. What is the error you are getting and on which line? What I see is just the normal output from your script?

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 

Andrew Wolpers March 14, 2023

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!

0 votes
Radek Dostál
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.
March 2, 2023

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.

Andrew Wolpers March 6, 2023

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.

TAGS
AUG Leaders

Atlassian Community Events