Requirement: Write a workflow validator to check if a selected custom field has the value set to Yes.

There are already plugins that can perform this. I have just considered this as an example.

  1. Create a plugin skeleton.

    • groupId: com.jiradev.jira.plugins
    • artifactId: field-value-validator
    • version: 1.0

  2. Cleanup the atlassian-plugin.xml. Then create a jira plugin module.

    • Select option 34 for workflow validator
    • Enter class name: FieldValueValidator
    • Enter package name: com.jiradev.jira.plugins.validators
    • Show advanced setup: N
    • Add another plugin module: N

  3. Take a look at the atlassian-plugin.xml. Delete the test cases generated by the SDK.
    We will not be covering test cases in these tutorials. I will be adding a separate series of tutorials for testing in the near future.

    Your project now contains 4 new files. We will be modifying each of these files to implement our logic and display content we want to display.

    • FieldValueValidator.java
    • FieldValueValidatorFactory.java
    • field-value-validator-input.vm
    • field-value-validator-view.vm


FieldValueValidatorFactory.java

Take a look at the code in the files here. You will see a lot of similarity between the code we wrote for the workflow condition and the workflow validator.

package com.jiradev.jira.plugins.validators;

import com.atlassian.core.util.map.EasyMap;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.fields.CustomField;
import com.opensymphony.workflow.loader.AbstractDescriptor;
import com.opensymphony.workflow.loader.ValidatorDescriptor;
import com.atlassian.jira.plugin.workflow.WorkflowPluginValidatorFactory;
import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
import java.util.Map;

public class FieldValueValidatorFactory extends AbstractWorkflowPluginFactory implements WorkflowPluginValidatorFactory
{
    protected void getVelocityParamsForInput(Map velocityParams)
    {
        //the default message
        velocityParams.put("customFields", ComponentAccessor.getCustomFieldManager().getCustomFieldObjects());
    }

    protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor)
    {
        getVelocityParamsForInput(velocityParams);
        getVelocityParamsForView(velocityParams, descriptor);
    }

    protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor)
    {
        if (!(descriptor instanceof ValidatorDescriptor))
        {
            throw new IllegalArgumentException("Descriptor must be a ValidatorDescriptor.");
        }

        ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
        String customFieldId = validatorDescriptor.getArgs().get("selectedCustomField").toString();
        CustomField customField = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(customFieldId);
        velocityParams.put("requiredValue", validatorDescriptor.getArgs().get("requiredValue"));
        velocityParams.put("selectedCustomField", validatorDescriptor.getArgs().get("selectedCustomField"));
        velocityParams.put("selectedCustomFieldName", customField.getName());
    }

    public Map getDescriptorParams(Map validatorParams)
    {
        // Process The map
        String requiredValue = extractSingleParam(validatorParams, "requiredValue");
        String selectedCustomField = extractSingleParam(validatorParams, "selectedCustomField");
        return EasyMap.build("requiredValue", requiredValue,"selectedCustomField",selectedCustomField);
    }
}

field-value-validator-input.vm

We will now setup the view where the user selects the custom field and enters a value that is required for the condition to pass.

<tr>
    <td class="fieldLabelArea">
        Custom Field:
    </td>
    <td nowrap>
        <select name="selectedCustomField" id="selectedCustomField">
            #foreach($customField in $customFields)
                <option name="$customField.getId()" id="$customField.getId" value="$customField.getId()"
                    #if($customField.getId() == $selectedCustomField)
                        selected
                    #end
                >$customField.getName()</option>
            #end
        </select>
    </td>
</tr>
<tr>
    <td class="fieldLabelArea">
        Required Value:
    </td>
    <td nowrap>
        <input name="requiredValue" id="requiredValue" value="${textutils.htmlEncode($requiredValue)}" />
    </td>
</tr>	

The code above is responsible for rendering the below screenshot.


field-value-validator-view.vm

Custom field <b>$selectedCustomFieldName</b> must have the value <b>$requiredValue</b>

The code above is responsible for rendering the below screenshot.


FieldValueValidator.java

package com.jiradev.jira.plugins.validators;

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.fields.CustomField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.atlassian.jira.issue.Issue;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.Validator;
import com.opensymphony.workflow.InvalidInputException;
import java.util.Map;

public class FieldValueValidator implements Validator
{
    public void validate(Map transientVars, Map args, PropertySet ps) throws InvalidInputException
    {
        String selectedCustomField = (String) args.get("selectedCustomField");
        String requiredValue = (String) args.get("requiredValue");
        Issue issue = (Issue) transientVars.get("issue");
        CustomField customField = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(selectedCustomField);
        Object value = issue.getCustomFieldValue(customField);
        if(value==null)
        {
            throw new InvalidInputException(ComponentAccessor.getCustomFieldManager().getCustomFieldObject(selectedCustomField).getName() + " must contain the value '" + requiredValue + "'.");
        }
        else
        {
            if(!value.toString().equals(requiredValue))
            {
                throw new InvalidInputException(ComponentAccessor.getCustomFieldManager().getCustomFieldObject(selectedCustomField).getName() + " must contain the value '" + requiredValue + "'.");
            }
        }
    }
}

Testing the plugin

  1. Create a text custom field, and a new workflow. In one of the transitions, add the Field Value validator.



  2. In my example, I have a text field named Analysis Complete? and it has to contain the value Yes in order to pass the condition.
  3. Associate the workflow to a workflow scheme. Create a project that uses the workflow scheme.
  4. Try out the workflow and debug the code and check if your validator is working.


Share


Show your support by tweeting about this tutorial. Is Jiradev something you would reommend? Let me know.

Contribute


Do you have a Jira plugin tutorial that can be used on this site? Please do share it with me and I can add it along with the ones available.

Contribute

Feedback


Your feedback can help improve the content on this site. If you have anything that you would like me to change/implement on the site.