Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

Simple scripted validator - Stuck on multi-field / custom user picker validation

Michael
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 31, 2023

Hello all,

We are trying to ensure that one of several specific users is selected within a User Picker (single user) customfield; and that the specific user selected is within the department selected within a previous Select List (Single Choice) customfield present to the user.

For example:

We have a Select List (Single Choice) customfield called Dept which contains all departments within our group. This field asks the submitter to select their corresponding department.

After the user selects the department they are within, a User Picker (single user) customfield named Internal Approver shows up and allows the submitter to select a single user. We only want them to select from one of the following users:
1) Bob Smith (Username = bobSm)

2) Michael M (Username = micmal)

3) Test_user (Username = TestUser1)

 

I've been trying to create a script that handles all of this, but I can't seem to get it to work. So far I have the following:

cfValues['Dept'] == 'Dept1' && cfValues['Internal Approver'] == 'Bob Smith' || cfValues['Internal Approver'] == 'Michael M' || cfValues['Internal Approver'] == 'Test_User'

Can someone help me here?

Thanks,

Mike

 

Conclusion:

TLDR for those who don't want to read the back and forth. I wanted to have a validator within my create screen which allowed me to use a Select List (Single Choice) customfield to allow only specific "approvers" per value selectable within the afore mentioned Select List (Single Choice) customfield.

The code below is what we came up with:

import com.atlassian.jira.component.ComponentAccessor
import com.opensymphony.workflow.InvalidInputException

Map<String, List<String>> departmentApproverMap = [
Dept1: ['bobSm', 'micmal', 'TestUser1'],
Dept2: ['jwillians', 'tessk'],
//etc
]

def deptCf = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName('Dept')[0]
def approverCf = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName('Internal Approver')[0]
def approverName = issue.getCustomFieldValue(approverCf)?.name as String
def deptValue = issue.getCustomFieldValue(deptCf)?.value as String

//add some code here is you need to do something if either dept or approver fields are empty
//For example
if (!deptValue) {
throw new InvalidInputException(deptCf.id, 'You must select the Department')
}
if (!approverName) {
throw new InvalidInputException(approverCf.id, 'You must select the approver')
}

def validApprovers = departmentApproverMap[deptValue] as List<String>
if(!validApprovers){
log.warn "The script does not contain approval mapping for deparment: $deptValue"
return
}
if (!validApprovers.contains(approverName)) {
def approverUserFullNames = validApprovers.findResults {userName->
def user = ComponentAccessor.userManager.getUserByName(userName)
if(!user){
log.warn "The script contains an invalid username: $userName"
return null
}
return user.displayName
}
throw new InvalidInputException(deptCf.id, "You selected a user that is not in the approved list: ${approverUserFullNames.join(', ')}")
}

2 answers

1 accepted

Suggest an answer

Log in or Sign up to answer
2 votes
Answer accepted
PD Sheehan
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 31, 2023

For single select fields, cfValues['Dept'] will give you an instance of an Option object.

To caompare it to a it to a string, you have to look a the getValue method.

Simliar thing for the userPicker. Those will be instanced of ApplicationUser.

If you are not sure, you can output the getClass() to the log then lookup in the details.

Try something like this

def approverName = cfValues['Internal Approver'].name
def deptValue = cfValues[
'Dept'].value == 'Dept1'
deptValue == 'Dept1' && approverName in [
'bobSm', 'micmal, 'TestUser1']
Michael
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 31, 2023

Hello @PD Sheehan

For some reason this did not work. I've made sure to add the additional ' behind the micmal user name, but for some reason the validation always seems to come up false.

I've looked at the workflow log, and all relating values within the needed fields are correct with the exception of the Internal Approver name. The value within that field has both the username as well as their ID within it. Example: "micmal (JIRAUSER12345)".

Could this by why it's failing?

Thanks,

Mike

PD Sheehan
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 31, 2023

I think I missed that it was a multi-select.

In which case the value is an array of options.

To know the exact class returned, you need to manually output something to the log

e.g. 

log.info "The value of 'Dept' is of type: ${cfValues[Dept].getClass()}"

I think this should work

def approverName = cfValues['Internal Approver'].name
def deptValues = cfValues[
'Dept'].value

'Dept1' in deptValues && approverName in [
'bobSm', 'micmal', 'TestUser1']
Michael
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 31, 2023

Hi again @PD Sheehan

The "Dept" field is a single select. and the Internal Approver field is "single select user picker".

I should have stated up above, but I'm also looking to have specific users be the only "valid" choice for specific departments selected within the "Dept" single select drop down field.

Example:

Dept 1 = Bob Smith, Michael M, Test_User

Dept 2 = James Williams, Tess K

Dept 3 = Matt Smith, Joe Moe

 

I would assume that this would change the requirements of my scripting quite a bit correct?

Thanks,

Mike

PD Sheehan
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 31, 2023

Ah and if you will have multiple combinations to check try something like this:

The question however is, which approvers are allowed when multiple departments are selected? If any approver from any of the depatments, you can try this:

def departmentApproverMap = [
Dept1 : ['bobSm', 'micmal', 'TestUser1'],
Dept2 : ['jwillians', 'tessk'],
//etc
]

def approverName = cfValues['Internal Approver'].name
def deptValues = cfValues[
'Dept'].value

deptValues.any{dept->
approverName in departmentApproverMap[dept]
}
PD Sheehan
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 31, 2023

Scratch all this, I need more caffein this morning.

I read "multi-field" and assumed multi select.

Let's try this again:

def departmentApproverMap = [
Dept1 : ['bobSm', 'micmal', 'TestUser1'],
Dept2 : ['jwillians', 'tessk'],
//etc
]

def approverName = cfValues['Internal Approver'].name
def deptValue = cfValues[
'Dept'].value


approverName in departmentApproverMap[deptValue]

Michael
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 31, 2023

Thanks again @PD Sheehan

This is working as I need it; and now I have an additional question - Sorry!

I want to display a different error message per "dept" group if the user selected isn't in the "approved group".

Example:

If "Dept1" was selected; I wanted to display something like "You selected a user that is not Bob smith, michael m, OR Test User 1 - Please select a user from this grouping to continue.

If "Dept2" was selected; I wanted to display a similar message but with the other names within the "approved group" (IE: You selected a user that is not James Williiams OR Tess K - Please select a user from this grouping to continue.)

Is there a way to set specific error messages per user group? And if I do that, would this script need to be moved out of the "simple scripted validator" and into the "Custom script validator" option?

PD Sheehan
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 31, 2023

You'll have to switch from a "Simple Script Validator" to "Custom Script Validator":

And go into a bit more details

import com.atlassian.jira.component.ComponentAccessor
import com.opensymphony.workflow.InvalidInputException

Map<String,List<String>> departmentApproverMap = [
Dept1 : ['bobSm', 'micmal', 'TestUser1'],
Dept2 : ['jwillians', 'tessk'],
//etc
]

def approverName = cfValues['Internal Approver']?.name as String
def deptValue = cfValues['Dept']?.value as String

//add some code here is you need to do something if either dept or approver fields are empty
//For example
if(!deptValue){
throw new InvalidInputException('cusomtfield_xxxx','You must select the Department')
}
if(!approverName){
throw new InvalidInputException('cusomtfield_yyyy','You must select the approver')
}

def validApprovers = departmentApproverMap[deptValue] as List<String>

if(!validApprovers.contains(approverName)){
def approverUsers = validApprovers.collect{ComponentAccessor.userManager.getUserByName(it)}
throw new InvalidInputException('cusomtfield_xxxx', "You selected a user that is not in the approved list: ${approverUsers*.displayName.join(', ')}")
}

Be sure to replace xxxx and yyyy with the appropriate customfield id.

Michael
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 31, 2023

Hi again @PD Sheehan

Thanks for the continued help! When I input the code above into the custom script validator, I'm getting a [Static type checking] - The variable [cfValues] is undeclared for the cf[Values] within the script.

Would you know how to fix this?

Thanks,

mike

PD Sheehan
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 31, 2023

Ah of course, the cfValues quick access is not available in custom script configurations.

You have to fetch the customfield object first then call issue.getCustomFieldValue

Try this instead:

import com.atlassian.jira.component.ComponentAccessor
import com.opensymphony.workflow.InvalidInputException

Map<String, List<String>> departmentApproverMap = [
Dept1: ['bobSm', 'micmal', 'TestUser1'],
Dept2: ['jwillians', 'tessk'],
//etc
]

def cfm = ComponentAccessor.customFieldManager

def
deptCf = cfm.getCustomFieldObjectsByName('Dept')[0]
def approverCf = cfm.getCustomFieldObjectsByName('Internal Approver')[0]

def
approverName = issue.getCustomFieldValue(approverCf)?.name as String
def deptValue = issue.getCustomFieldValue(deptCf)?.value as String

//add some code here is you need to do something if either dept or approver fields are empty
//For example
if (!deptValue) {
throw new InvalidInputException(deptCf.id, 'You must select the Department')
}
if (!approverName) {
throw new InvalidInputException(approverCf.id, 'You must select the approver')
}

def validApprovers = departmentApproverMap[deptValue] as List<String>

if (!validApprovers.contains(approverName)) {
def approverUsers = validApprovers.collect { ComponentAccessor.userManager.getUserByName(it) }
throw new InvalidInputException(deptCf.id, "You selected a user that is not in the approved list: ${approverUsers*.displayName.join(', ')}")
}
Michael
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 31, 2023

Hi again @PD Sheehan 

When I try to create an issue where this validator is being used, it allows all users to be selected, and not just the groupings we added to the script. It also gave me an error when I look at the workflow validator log.

I'm trying to add the error to this comment, but the forums are giving me an "html" error constantly...

The error within the workflow states The workflow script has failed for 'jwillians' and that there is a java lang NullPointerException - Cannot invoke method contains on null object.

Does this error make sense to you?

Thanks again for all the continued help!

Mike

PD Sheehan
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 31, 2023

You'll notice that like 5,6,7 are not necessarily complete.

You need to make sure you map all your departments with the correct list of user names.

In this case, I invented "jwillians" based on the full name you provided. You have to replace with the correct username.

I modified the script to validate things and provide some better feedback in the logs:

import com.atlassian.jira.component.ComponentAccessor
import com.opensymphony.workflow.InvalidInputException

Map<String, List<String>> departmentApproverMap = [
Dept1: ['bobSm', 'micmal', 'TestUser1'],
Dept2: ['jwillians', 'tessk'],
//etc
]

def deptCf = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName('Dept')[0]
def approverCf = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName('Internal Approver')[0]
def approverName = issue.getCustomFieldValue(approverCf)?.name as String
def deptValue = issue.getCustomFieldValue(deptCf)?.value as String

//add some code here is you need to do something if either dept or approver fields are empty
//For example
if (!deptValue) {
throw new InvalidInputException(deptCf.id, 'You must select the Department')
}
if (!approverName) {
throw new InvalidInputException(approverCf.id, 'You must select the approver')
}

def validApprovers = departmentApproverMap[deptValue] as List<String>
if(!validApprovers){
log.warn "The script does not contain approval mapping for deparment: $deptValue"
return
}
if (!validApprovers.contains(approverName)) {
def approverUserFullNames = validApprovers.findResults {userName->
def user = ComponentAccessor.userManager.getUserByName(userName)
if(!user){
log.warn "The script contains an invalid username: $userName"
return null
}
return user.displayName
}
throw new InvalidInputException(deptCf.id, "You selected a user that is not in the approved list: ${approverUserFullNames.join(', ')}")
}
Michael
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 31, 2023

Hi again @PD Sheehan

Thanks for the tip! I was using an existing username, I just put the jwillians name as an example. I must have either miss-typed the name, or left a space in the name by accident. This code is working splendidly for me now!

Thank you VERY much for all your time. Have a great day ahead!

~mike

0 votes
Sayed Bares _ServiceRocket_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
July 31, 2023

@Michael 

When querying single select list you will need to access its value property and if you are checking the username of the single user picker field then you need to query the username property. Additionally, the logic should be an OR operator instead of an AND if you want to pass validator for other departments (dept2, dept3 ...). Can you try this code and see if it works?

cfValues['Dept']?.value != 'Dept1' || cfValues['Internal Approver']?.getUsername() in ['Bob Smith', 'Michael M', 'Test_User']

If you only want to pass the validator if the dept1 is selected and its usernames then use an AND operator:

 

cfValues['Dept']?.value == 'Dept1' && cfValues['Internal Approver']?.getUsername() in ['Bob Smith', 'Michael M', 'Test_User']

The above query will fail the validator if other Depts are selected or other Internal Approvers are selected

Michael
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 31, 2023

Hello @Sayed Bares _ServiceRocket_ ,

I should have originally stated that I want to setup this validator up for several different groups of people / departments; seeing as how I'm assuming that changes a lot about the script.

If I want to setup specific people per Department; for example:
Dept 1 = Bob Smith, Michael M, Test_User

Dept 2 = James Williams, Tess K

Dept 3 = Matt Smith, Joe Moe

How would I go about doing this?

Thanks,

Mike

Sayed Bares _ServiceRocket_
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
July 31, 2023

@Michael just add an OR operation or use @PD Sheehan suggestion either way should work :)

 

(cfValues['Dept']?.value == 'Dept1' && cfValues['Internal Approver']?.getUsername() in ['Bob Smith', 'Michael M', 'Test_User']) || (cfValues['Dept']?.value == 'Dept2' && cfValues['Internal Approver']?.getUsername() in ['James Williams', 'Tess K']) || (cfValues['Dept']?.value == 'Dept3' && cfValues['Internal Approver']?.getUsername() in ['Matt Smith', 'Joe Moe'])
TAGS
AUG Leaders

Atlassian Community Events