Tuesday, 17 July 2012

Create a post function which needs to be called on Jira workflow transition.

Have you ever wondered, why there are few post functions available in the Jira platform while transitioning between statuses of an issue workflow?  Of course, I felt while I am working with Jira platform.

Today we will look how we can add post function which can be listed in transition states of jira workflow. Fyi, Jira platform uses Open Symphony (OS) workflow api in it's architecture which will take care of  issue transition states. To know more about OS workflow API please visit here

Following are the various steps required to implement to create a post function.

1. Create a class "ModifyCustomFieldPF" which incorporates the implementation of business logic. Whenever this post-function is called, execute behavior will be executed by the Jira platform and it's implementation class "ModifyCustomFieldPFImpl" as shown below.

package com.company.application.module.postfunction;

import java.util.Map;

import com.atlassian.jira.issue.MutableIssue;

import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.FunctionProvider;
import com.opensymphony.workflow.WorkflowException;

public class ModifyCustomFieldPF implements FunctionProvider {
      public static final String ISSUE = "issue";

      public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
            MutableIssue mutableIssue = (MutableIssue) transientVars.get(ISSUE);
            // here implement the required business logic
      }
}



package com.company.application.module.postfunction;


import java.util.HashMap;
import java.util.Map;

import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
import com.opensymphony.workflow.loader.AbstractDescriptor;

public class ModifyCustomFieldPFImpl extends AbstractWorkflowPluginFactory implements WorkflowPluginConditionFactory

{
   
    public ModifyCustomFieldPFImpl() {
        super();
        // TODO Auto-generated constructor stub
    }

    protected void getVelocityParamsForEdit(Map arg0, AbstractDescriptor arg1) {
        // TODO Auto-generated method stub

    }

    protected void getVelocityParamsForInput(Map arg0) {
        // TODO Auto-generated method stub

    }

    protected void getVelocityParamsForView(Map arg0, AbstractDescriptor arg1) {
        // TODO Auto-generated method stub

    }

    public Map getDescriptorParams(Map arg0) {
        // TODO Auto-generated method stub
        return new HashMap();
    }

}   



2. Create a velocity template(ModifyCustomFieldPF.vm) file


Ensures that CustomFieldValue will be modified......



3. Entries in the atlassian-plugin.xml file.


<workflow-function key="ModifyCustomFieldPF" name="ModifyCustomFieldPF"   class="com.company.application.module.postfunction.ModifyCustomFieldPFImpl">
        <description>Description of your post function plug-in.</description>
        <function-class>"com.company.application.module.postfunction.ModifyCustomFieldPF</function-class>
        <orderable>true</orderable>
        <unique>true</unique>
        <deletable>true</deletable>
        <editable>true</editable>
        <resource type="velocity" name="view" location="templates/ModifyCustomFieldPF.vm"/>
    </workflow-function>      


4. Now build your plug-in and deploy the artifact and restart the application server.

Go to any workflow listed in Jira platform and check "Ensures that CustomFieldValue will be modified....." will be visible in the post function tab of the selected workflow transition.

Monday, 16 July 2012

How to save Custom Field values?

Today, we will see, the ways to save values in Jira Custom Fields. Firstly, let us understand what is Custom Field in Jira platform.

Custom Fields is the additional feature given by the Jira platform to tailor Jira's platform for your organization needs. There are nearly 20 types of Custom Field Types available in Jira platform and saving values in custom fields depends on custom field's type.

Ex:-1
Let's suppose, there is a requirement to save a "Free Text" type custom field of name "Description", then

"Description" is the custom field name
"ABCD....XYZ" is the description value to be stored.


CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
CustomField customField =  getCustomFieldObjectByName("Description");
issue.setCustomFieldValue(customField, "ABCD....XYZ");


Ex:- 2
Let's suppose, there is a requirement to save a "Select Type" custom field type.

"Severity" is the custom field name and has options as "Critical", "High", "Medium" and "Low"
"High" is the custom field value to be stored.


public void updateCustomFieldValue(MutableIssue issue, String customFieldName, Object customFieldValue) {

CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
CustomField customField =  getCustomFieldObjectByName("Description");
String customFieldType = customField.getCustomFieldType().getName();
if (customFieldType.equalsIgnoreCase("Select Type")) {
List<Option> options = getOptionsManager().findByOptionValue((String)customFieldValue);
            if (options == null || options.isEmpty()) {
                logger.error("Can't set custom field value to " + customFieldValue + " because it doesn't correspond to a valid custom field option of the custom field : " + customField.getName());
            } else {
                issue.setCustomFieldValue(customField, options.get(0));
            }
}

}

private OptionsManager getOptionsManager() {
        return  ((OptionsManager) ComponentManager.getInstance().getComponentInstanceOfType(OptionsManager.class));
    }


Above mentioned scenario is valid for custom field types such as "Multi Checkboxes" and "Select Type"

Ex:- 3
Let's suppose, there is a requirement to save a "Cascading Select" custom field type.

State/District are the cascading select drop down list, based on the selection of state value, districts drop-down will be populated.

State/District is the custom field name;


/**
 * This method is to populate custom field value from "Cascading Select" type custom fields.
 * 
 **/
public static Map<String, String> populateStateDistrictMap(
            MutableIssue issue) {
        Map<String, String> stateDistrictmap = new HashMap<String, String>();
        CustomField stateDistrictCF=  getStateDistrictCF();

        Object stateDistrictCFValue= issue
                .getCustomFieldValue(stateDistrictCF);
        CustomFieldParams params = new CustomFieldParamsImpl(stateDistrictCF, stateDistrictCFValue);
        String stateCFValue = null;
        String districtCFValue = null;
        if (params != null && !params.isEmpty()) {
            stateCFValue = params.getFirstValueForNullKey().toString();
            districtCFValue = params.getFirstValueForKey("1").toString();
        }
        stateDistrictmap.put("STATE", stateCFValue);
        stateDistrictmap.put("DISTRICT", districtCFValue);

         return stateDistrictmap;
}


public static boolean updateStateDistrictMap(MutableIssue issue, String stateStr, String districtStr) {
        List<Option> stateCFOption = getOptionsManager().findByOptionValue(stateStr);
        List<Option> districtCFOption = getOptionsManager().findByOptionValue(districtStr);
        Map<String, Option> map = new HashMap<String, Option>();
        map.put((String) null, stateCFOption .get(0));
        map.put("1", districtCFOption .get(0));
        CustomField stateDistrictCF=  getStateDistrictCF(); 
        issueToCreate.setCustomFieldValue(stateDistrictCF, map);
}

private CustomField getStateDistrictCF(){
        CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
        CustomField stateDistrictCF=  getCustomFieldObjectByName("State/District"); 
        return  stateDistrictCF;
}

private OptionsManager getOptionsManager() {
        return  ((OptionsManager) ComponentManager.getInstance().getComponentInstanceOfType(OptionsManager.class));
    }