ScriptRunner Behaviours: Synchronize custom field options

Jordan Packer July 6, 2016

I have two custom fields (select lists) that need to have the same list of options at any given time. We'll consider one to be the "master" field where all changes are made, and the other to be "slave" field that will get updated anytime the master is changed. I tried to build this as a Scripted Listener, but it turns out that there isn't a reliable "Custom Field Updated" event for this, so I'm doing it as a Behavior instead. Basically, anytime the slave field shows up on a screen, it will run the Behavior script to make sure it is in sync with the master field.

I have all of my code working except for one weird bug.... Upon loading the screen with the slave field on it, I can see that the custom field options are updated correctly (by looking at the custom field configuration in the admin area), but when I look at the select list options on the screen, they're still a little off (the new option will be there, but not in the right spot, or the disabled options from the master field will still be showing up in the slave field options). If I close out of the screen and load it again, then everything looks great (I assume this is because it is pulling the options straight from the custom field context this time instead of using the setFieldOptions method). What I want to know is, why does the setFieldOptions method appear to not be working correctly? I can even debug the "ops" Map right before I call the setFieldOptions method and see that it has everything I need and in the correct order, but yet when I look in the UI it's all out of wack that first time. @Jamie Echlin [Adaptavist], do you have any ideas?

Here's the code I'm using, for reference (I know there are probably a billion ways to code this in a better way, but let's focus on my bug and not on syntax efficiencies for the moment smile ):

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.customfields.option.Option
def optionsManager = ComponentAccessor.getOptionsManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cf_maintenanceRelease = customFieldManager.getCustomFieldObject(17065L)
def cf_mrLocked = customFieldManager.getCustomFieldObject(17066L)
def mrFieldConfig = cf_maintenanceRelease.getRelevantConfig(getIssueContext())
def mrLockedFieldConfig = cf_mrLocked.getRelevantConfig(getIssueContext())
def originalOps = optionsManager.getOptions(mrFieldConfig)
def currentMrLockedOps = optionsManager.getOptions(mrLockedFieldConfig)
def originalOpsValuesArray = []
def originalOpsDisabledArray = []
def currentMrLockedOpsValuesArray = []
def currentMrLockedOpsDisabledArray = []
def originalOpsSize = originalOps.size()
def ops = ["-1":"None"]
def mrLockedFormField = getFieldByName("MR Locked")
//Get an array of strings for each list of options
originalOps.each{
 originalOpsValuesArray << it.getValue()
 originalOpsDisabledArray << it.getDisabled()
}
currentMrLockedOps.each{
 currentMrLockedOpsValuesArray << it.getValue()
 currentMrLockedOpsDisabledArray << it.getDisabled()
}
//If the two lists don't match, then sync 'em up
if(originalOpsValuesArray != currentMrLockedOpsValuesArray || originalOpsDisabledArray != currentMrLockedOpsDisabledArray){
    if(originalOpsValuesArray != currentMrLockedOpsValuesArray){
        //Add values that are missing
        for(def i=0; i<originalOpsSize; i++){
            if(!currentMrLockedOpsValuesArray.contains(originalOpsValuesArray[i])){
                currentMrLockedOpsValuesArray.add(i, originalOpsValuesArray[i])
                currentMrLockedOps.addOption(null, originalOps[i].getValue())
            }
        }
        
        //Remove extraneous values
        def tmpSize = currentMrLockedOpsValuesArray.size()
        for(def i=0; i<tmpSize; i++){
            if(!originalOpsValuesArray.contains(currentMrLockedOpsValuesArray[i])){
                currentMrLockedOpsValuesArray.remove(i)
                tmpSize--
                currentMrLockedOps.removeOption(currentMrLockedOps[i])
            }
        }
        
        //Get the fresh set of options
        def updatedMrLockedOps = optionsManager.getOptions(cf_mrLocked.getRelevantConfig(getIssueContext()))
        //Create a map for the order sequence
        Map positionMap = [:]
        for(def i=0; i<originalOpsSize; i++){
            positionMap.put(i, updatedMrLockedOps.find{ it.getValue() == originalOps[i].getValue() })
        }
        
        //Update the order sequence
        updatedMrLockedOps.moveOptionToPosition(positionMap)
    }
    if(originalOpsDisabledArray != currentMrLockedOpsDisabledArray){
        //Synchronize enabled/disabled
        for(def i=0; i<originalOpsSize; i++){
            if(originalOps[i].getDisabled()){
                optionsManager.disableOption(currentMrLockedOps.find{ it.getValue() == originalOps[i].getValue() })
            } else {
                optionsManager.enableOption(currentMrLockedOps.find{ it.getValue() == originalOps[i].getValue() })
            }
        }
    }
    //Update the Form Field
    def sortedOps = optionsManager.getOptions(cf_mrLocked.getRelevantConfig(getIssueContext()))
    sortedOps.each{
        ops.put(it.getOptionId() as String, it.getValue())
    }
    
    mrLockedFormField.setFieldOptions(ops)
}

1 answer

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

0 votes
Answer accepted
adammarkham
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.
July 7, 2016

It sounds like what your are describing is that the options you are setting are out of order. This was a bug and fixed from 4.3.2+ for JIRA 7 and 4.1.3.13+ for JIRA 6. Please see this issue for more details.

You should update to the latest version of ScriptRunner on marketplace for your version of JIRA which you can find here.

Jordan Packer July 7, 2016

Thanks Adam, I'll work on getting the latest plugin version installed.

Jordan Packer July 7, 2016

I just upgraded to the latest version (4.3.4) but it's still not working as expected. In fact, some of the functionality has regressed further. Now, when I add an option to the master field and look at the slave field options on the create screen, the new option doesn't show up at all (before, it would show up but it wouldn't be in the right place). Like before, if I close out of the create screen and bring it up again, then the slave options look fine. But for some reason the setFieldOptions() method does not appear to be working appropriately.

Jordan Packer July 8, 2016

Nevermind, I found a different issue in my script that was not preventing the code to reach the bottom. Once I fixed that, I also changed the ops Map to be of type LinkedHashMap (to keep everything in the order it was submitted, instead of ordering on the key) and it appears to be working! Thanks for your help on this.

adammarkham
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.
July 8, 2016

No problem. Glad you got it sorted. 

TAGS
AUG Leaders

Atlassian Community Events