Search code examples
jiradynamic-datajira-pluginjira-rest-java-api

Jira Plugin: Dynamically Populating a Select Custom Field via Database


I have been trying to make a Jira plugin using Java to try and make a SelectCFType get populated dynamically by using a database. I currently have a working SelectCFType from some code I found here. The only issue is I have no idea where to start when it comes to populating it via a database. I tried to manually populate it once, but Jira just gave me an error when creating a ticket because the stored values in its internal database for the Custom Field and the ones I provided were different. Any help would be greatly appreciated!

Java class

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.customfields.impl.SelectCFType;
import com.atlassian.jira.issue.customfields.manager.OptionsManager;
import com.atlassian.jira.issue.customfields.option.Option;
import com.atlassian.jira.issue.customfields.option.Options;
import com.atlassian.jira.issue.fields.config.FieldConfigScheme;
import com.atlassian.jira.issue.fields.rest.json.beans.JiraBaseUrls;
import com.atlassian.jira.issue.search.SearchContextImpl;
import org.apache.commons.collections.MultiMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.fields.config.FieldConfig;
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem;
import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;

import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DynamicSelectCF extends SelectCFType{
        private static final Logger log = LoggerFactory.getLogger(DynamicSelectCF.class);
        private final OptionsManager optionsManager;
    
        private final JiraBaseUrls jiraBaseUrls;
        @Inject
        public DynamicSelectCF(@JiraImport CustomFieldValuePersister customFieldValuePersister,
                               @JiraImport OptionsManager optionsManager,
                               @JiraImport GenericConfigManager genericConfigManager,
                               @JiraImport JiraBaseUrls jiraBaseUrls){
            super(customFieldValuePersister, optionsManager, genericConfigManager, jiraBaseUrls);
            this.optionsManager = optionsManager;
            this.jiraBaseUrls = jiraBaseUrls;
        }
    
        @Override
        public Map<String, Object> getVelocityParameters(final Issue issue,
                                                         final CustomField field,
                                                         final FieldLayoutItem fieldLayoutItem) {
    
            final Map<String, Object> parameters = super.getVelocityParameters(issue, field, fieldLayoutItem);
    
            FieldConfig fieldConfiguration = null;
    
            if(issue == null)
            {
                fieldConfiguration = field.getReleventConfig(new SearchContextImpl());
            } else
            {
                fieldConfiguration = field.getRelevantConfig(issue);
            }
            Options options = this.optionsManager.getOptions(fieldConfiguration);
            if (options.isEmpty()) {
                this.optionsManager.createOption(fieldConfiguration, null, new Long(1), "A");
                this.optionsManager.createOption(fieldConfiguration, null, new Long(2), "B");
            }
            options = this.optionsManager.getOptions(fieldConfiguration);
            Map<Long, String> results = new HashMap<Long, String>();
    
            Long selectedId= (long) -1;
            boolean selected = false;
            Object value = field.getValue(issue);
            if (value!=null) {
                selected=true;
            }
            for (Option option : (Iterable<Option>) options) {
                results.put(option.getOptionId(), option.getValue());
                if (selected && value.toString().equals(option.getValue())) {
                    selectedId = option.getOptionId();
                }
            }
    
            parameters.put("results", results);
            parameters.put("selectedId", selectedId);
            return parameters;
        }
    }

edit.vm (for web-resource)

#* @vtlvariable name="results" type="java.util.Map" *#
#* @vtlvariable name="selectedId" type="java.lang.String" *#
#controlHeader ($action $customField.id $customField.name $fieldLayoutItem.required $displayParameters.noHeader)

<select name="$customField.id" id="$customField.id" >
<option value="-1">Not selected</option>
#foreach ($mapEntry in $results.entrySet())
#if ( $selectedId == $mapEntry.key )
<option selected="selected" value="$mapEntry.key">$mapEntry.value</option>
#else
<option value="$mapEntry.key">$mapEntry.value</option>
#end
#end
</select>

#controlFooter ($action $fieldLayoutItem.fieldDescription $displayParameters.noHeader)

Solution

  • So I think I figured out a good way to do it. I created a method and so far it has been working very well.

    public void populatingOprions(FieldConfig fieldConfiguration, ArrayList<String> optionPopulation){            
            for (int i = 0; i < population.size(); i++){
                this.optionsManager.createOption(fieldConfiguration, null, new Long(i), optionPopulation.get(i));
            }
            hasBeenCalled = true;
        }
    

    In the getVelocityParameters method I added this code under the if(options.isEmpty()) statement

    if(!hasBeenCalled) {
        String arr[] = {"---------------", "This", "Is", "Just", "A", "Test", "For", "Population"};
        for(int i = 0; i < arr.length; i++){
            population.add(arr[i]);
        }
        populatingOprions(fieldConfiguration, population);
    }
    

    I have an ArrayList that hold all the values and I think we have a mySQL database wich appears to be pretty easy to get the data from. hasBeenCalled is a boolean that tracks if the method got called already to populate the SelectCF's options so it doesn't duplicate them.