Simple Scripted Validators
Use the Simple scripted validator to run a simple embedded script that determines whether an issue should be permitted to transition to a particular status within a workflow. This validator allows you to write a Groovy script that can evaluate a wide range of conditions based on issue fields, workflow states, project properties, user permissions, and other contextual data. If validation fails, you can provide a custom error message that explains why the transition is not allowed.
This validator is particularly useful for teams with unique process requirements that need to enforce specific criteria for issue lifecycle management.
Use this validator
You can now test to see if this workflow condition works.
Examples
Each of the examples below is based on specific workflows. Make sure you adjust the examples appropriately to suit your workflow/s.
- Require a fix version if the resolution value is fixed
- Check the resolution value
- Require a comment
- Require a comment when the assignee is changed
- Make sure an option is selected for a custom field
- Make sure no other sub-tasks have the same value for a custom field on issue creation
- Check for attachments
- Check the status or properties of sub-tasks
- Check linked issues
- Check the values of a cascading select field
Require a fix version if the resolution value is fixed
You can use the following script to make sure that when a user sets the resolution to Fixed, they also have to add the correct Fix Version:
issue.resolution.name != "Fixed" || issue.fixVersionsThe above example could be set up as follows:
When a fix version is not provided, the error appears as follows:
Check the resolution value
You can use the following script to validate that a particular resolution value is chosen:
issue.resolution?.name == 'Not an Issue'You might want to combine this validator with a check on the action name.
The above example could be set up as follows:
When a fix version is not provided, the error appears as follows:
Require a comment
You can use the following script to make sure a comment is added when the resolution of Won't Fix is selected:
issue.resolution?.name != "Won't Fix" || transientVars["comment"]The above example could be set up as follows:
When Won't fix is selected, and a comment isn't provided, the error appears as follows:
Require a comment when the assignee is changed
You can check for changed fields by calling methods on originalIssue. This is the issue as it was before modifications made (during this transition) were applied.
You can use the following script to make sure a comment is added when the assignee is changed during a transition:
originalIssue.assigneeId == issue.assigneeId || transientVars["comment"]The above example could be set up as follows:
When the assignee is changed and a comment isn't provided, the error appears as follows:
Make sure an option is selected for a custom field
- Multiselect values are a collection of
Optionobjects. cfValuesis a map-like structure where you can get the value of any custom field on the object. Note that the keys are the field name and not the id.
You can use the following script to make sure a certain option is selected from a multi-select custom field:
def values = cfValues['My Multi Select']
return values && (values*.value as Collection<String>).contains('A field value')You can use the following script to make sure an option is selected for a single-select or radio button field:
cfValues['My Single Select']?.value == "A field value"You can use the following script as a more complex example, where if the user sets the select-list field Demo to No, you require them to fill in a text field Reason for no demo:
cfValues['Demo']?.value != 'No' || cfValues['Reason for No Demo']You can use the following script to make sure a custom field has a value:
null or an empty value, it will be evaluated as false and your chosen error will display.cfValues['Custom Field Name']Make sure no other sub-tasks have the same value for a custom field on issue creation
You can use the following script to make sure, on the creation of a sub-task, that none of the other sub-tasks of the parent issue have the same value for a particular custom field:
if (!issue.isSubTask()) {
return true
}
def parent = issue.parentObject
def selectedValue = issue.getCustomFieldValue('SelectListA')
!parent.subTaskObjects.any { subtask ->
subtask.getCustomFieldValue('SelectListA') == selectedValue
}The above example could be set up as follows:
When the option selected is the same as another sub-task for the parent issue, the error appears as follows:
return, the result of the last statement executed defines whether the validator accepts or rejects the transition.Check for attachments
You can use the following script to check that at least one PDF file is attached:
issue.getAttachments().find { it.filename.endsWith(".pdf") }The attachment must have existed before the transition was applied to the issue. If you have the Attachments field on the screen, you cannot get newly added attachments in this way.
If your workflow requires specific attachments at certain stages, your validator can either:
- tell the user to cancel, add the attachment, and try again (or remove the Attachments field from the screen)
- use a different method to get newly added attachments.
The above example could be set up as follows:
When the transition is attempted without a PDF attachment associated with an issue, the error appears as follows:
Check the status or properties of sub-tasks
The following script examples can be used to check the status or properties of sub-tasks relative to their parent issue during a transition.
Make sure all subtasks are resolved
You can use the following script to make sure that all subtasks of an issue are resolved:
issue.subTaskObjects.every { it.resolution }Make sure at least one subtask is resolved
You can use the following script to make sure there is at least one subtask is resolved:
!issue.subTaskObjects || issue.subTaskObjects.any { it.resolution }Make sure the issue has at least one subtask which is In Progress
You can use the following script to make sure the issue has at least one subtask which is In Progress:
issue.subTaskObjects.any { it.status.name == "In Progress" }cfValues is not available, so you must use custom field manager to get hold of the custom field object, as shown in Make sure no other sub-tasks have the same value for a custom field on issue creation.Check properties of the parent issue
parentObject. You can use the following script to make sure the parent is In Progress:
!issue.isSubTask() || issue.parentObject.status.name == "In Progress"NullPointerException if the script is run on an issue that is not a sub-task, as standard issues do not have a parentObject.Check linked issues
You can use issueLinkManager to retrieve all outward links. For example, you could use the following to check that the issue has at least one outward Duplicate link:
issueLinkManager.getOutwardLinks(issue.id)*.issueLinkType.name.contains('Duplicate')You could expand on this example and use it practically. For example, you can use the following script to make sure an issue has at least one outward Duplicate link when the resolution is set to Duplicate:
issue.resolution.name != "Duplicate" ||
issueLinkManager.getOutwardLinks(issue.id)*.issueLinkType.name.contains('Duplicate')The above example could be set up as follows:
When the transition is resolved as Duplicate and a duplicate issue is not linked, the error appears as follows:
If you have the Issue Links field on a form and want to validate links that occur during this transition, check out the examples on the Validating Attachments/Links in Transitions page.
Check the values of a cascading select field
A cascading select field allows users to select a value from a parent drop-down and a related value from a child drop-down. In the following example scripts, cfValues is a map-like structure that holds the current values of custom fields.
You can use the following script to check that both drop-downs of a cascading select are filled:
cfValues["CascadingSelect"]?.keySet()?.size() == 2You can use the following script to check that the first dropdown of a cascading select is AAA and the second is a1:
import com.atlassian.jira.issue.customfields.option.LazyLoadedOption
cfValues['CascadingSelect'].values().collect { (it as LazyLoadedOption).value } == ['AAA', 'A1']You can use the following script to check the first option of the cascading select is AAA:
cfValues["CascadingSelect"]?.get(null)?.value == "AAA"You can use the following script to check the second option of the cascading select is a1:
cfValues["CascadingSelect"]?.get("1")?.value == "A1"The first example above could be set up as follows:
When the transition is attempted and the cascading select does not have both drop-down options filled, the error appears as follows: