Hello Community, I need help on scriptrunner, I need to set assignee based on the component's lead. Below is the code that I'm using
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.customfields.option.LazyLoadedOption
// a map with select list option to Component
def componentsMap = [
"BOSS" : "Component1",
"Mashery" : "Component2",
]
def issue = event.issue as MutableIssue
def selectList = ComponentAccessor.customFieldManager.getCustomFieldObjectByName("Affected Service")
def selectListValue = issue.getCustomFieldValue(selectList) as LazyLoadedOption
// there is no value for the select list - therefore do nothing
if (! selectListValue) {
return
}
def componentManager = ComponentAccessor.projectComponentManager
def componentName = componentsMap.get(selectListValue.value)
def component = componentManager.findByComponentName(issue.projectObject.id, componentName)
def userManager = ComponentAccessor.userManager
def components = issue.componentObjects.toList()
if (component) {
componentManager.updateIssueProjectComponents(issue, [component])
}
issue.setAssignee(ComponentAccessor.userManager.getUserByKey(component.lead))
In this code, I have Affected Service as a single select list, if BOSS has been selected, the Component Field will update and set to Component1, but the assignee remains unassigned even if I have default assignee on my component. I know that there's something wrong with my code because I'm a beginner. I need to change assignee everytime component field is changed, that's why I used script listener. Thanks
Hi Alvin
Assigning an issue with a script is more complex. Jira needs to check whether the assignment is valid. Therefore you have to use th issueService.
Replace your issue.setAssignee line with:
def issueService = ComponentAccessor.getIssueService()
def user = component.componentLead
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def assignmentValidation = issueService.validateAssign(currentUser, issue.id, user.key)
if (assignmentValidation.isValid()){
issueService.assign(user, assignmentValidation)
} else {
log.debug(assignmentValidation) // you probably have to loop through the ErrorCollection object
}
At the top of the script you can import loggers and set the level. It can be useful to have some logging when troubleshooting:
import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.yourjira.componentupdate")
log.setLevel(Level.DEBUG)
Hi @Charlie Misonne, Thank you for the very fast response. will try this now and update you. Thank you!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Charlie Misonne , it shows some error. can you help me?
2018-09-13 10:10:19,380 ERROR [runner.AbstractScriptListener]: ************************************************************************************* 2018-09-13 10:10:19,409 ERROR [runner.AbstractScriptListener]: Script function failed on event: com.atlassian.jira.event.issue.IssueEvent, file: <inline script> java.lang.NullPointerException: Cannot get property 'componentLead' on null object at Script1208.run(Script1208.groovy:37)
import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.yourjira.componentupdate")
log.setLevel(Level.DEBUG)
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.customfields.option.LazyLoadedOption
// a map with select list option to Component
def componentsMap = [
"BOSS" : "Component1",
"Mashery" : "Component2",
]
def issue = event.issue as MutableIssue
def selectList = ComponentAccessor.customFieldManager.getCustomFieldObjectByName("Affected Service")
def selectListValue = issue.getCustomFieldValue(selectList) as LazyLoadedOption
// there is no value for the select list - therefore do nothing
if (! selectListValue) {
return
}
def componentManager = ComponentAccessor.projectComponentManager
def componentName = componentsMap.get(selectListValue.value)
def component = componentManager.findByComponentName(issue.projectObject.id, componentName)
def userManager = ComponentAccessor.userManager
def components = issue.componentObjects.toList()
if (component) {
componentManager.updateIssueProjectComponents(issue, [component])
}
def issueService = ComponentAccessor.getIssueService()
def user = component.componentLead
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def assignmentValidation = issueService.validateAssign(currentUser, issue.id, user.key)
if (assignmentValidation.isValid()){
issueService.assign(user, assignmentValidation)
} else {
log.debug(assignmentValidation) // you probably have to loop through the ErrorCollection object
}
and this is the whole code , already applied your suggestions
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
That error means the component is null.
On your issue: do you selected a value for Affected Service?
And does Component1 and Component2 exist in your project?
It's probably safer if you put the assignment part in your if:
log.debug(component)
if (component) {
componentManager.updateIssueProjectComponents(issue, [component])
def issueService = ComponentAccessor.getIssueService()
def user = component.componentLead
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def assignmentValidation = issueService.validateAssign(currentUser, issue.id, user.key)
if (assignmentValidation.isValid()){
issueService.assign(user, assignmentValidation)
} else {
log.debug(assignmentValidation) // you probably have to loop through the ErrorCollection object
}
}
I also added log.debug(component)
You can check the execution log after updating your Affected Service field to check whether the component was found.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Charlie Misonne That works so perfectly ! Thank you for helping me! I missed the part on editing the component1, you're a life saver. Thank you!!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You're welcome @Alvin!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Charlie Misonne, I add Issued Updated on event to fire on because I think that's the way for the Assignee to change everytime Component/s field change. but I see that it neglects my edit and it goes back to the original issue posted. Any thoughts about this? Thank you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
So it was working a couple of hours ago but now it doesn't?
Issue Udpated is the correct event to use.
Have you selected your project in the Project Key picker?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Charlie Misonne, yep its working perfectly. Before, the event is only Issue Created and it works really well, then now I try to add Issue Updated, so there are two events now, Issue Created and Issue Updated. But whenever I tried to Edit Component/s field, (i.e. I change it to JIRA) , it just neglect my edit and sticks to what I've created. (if I create issue with Affected Services = BOSS , Component/s = Component1 , and assignee will be the component lead , it stays the same even if I edit it.)
But when I update issue with one event only (which is Issue Created) , it can be edited but thee Component/s Field woudn't change.
I just want to go deeper escalation with these Component/s , Is it possible ? Thank you!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Charlie Misonne , I found out that the Component/s , and the Assignee relies on the value of the Affected Service. Thank you for helping me ! I really appreciate it. Thanks!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Alvinglad it was resolved!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Charlie Misonne , I just have a quick question here, how can I map multiple attributes to one value?
Script below
def componentsMap = [
"BOSS" : "Component1",
"WORK" : "Component1",
"Class" : "Component1",
"Mashery" : "Component2",
]
I tried to make it like this, but no luck. Thank you
def componentsMap = [
["BOSS","WORK,"Class" ] : "Component1",
"Mashery" : "Component2",
]
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Apparenlty there is a MultikeyMap but I have absolutely no experience with it. http://www.tothenew.com/blog/multikeymap-nested-maps-solution/
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you @Charlie Misonne, will check on this
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ivan Tovbin I tried to use MultiKeyMap but it keeps on giving me unresolved class even if I import MultiKeyMap. Thank you
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I assume your idea is to be able to pull the required component by using ANY of the attributes, right? Problem with MultiKeyMap is that you have to provide a set of ALL attributes mapped to a value to get that value. Surely, it's possible to write a logic that would first search all attribute sets for the attribute you need and then use that set to pull a value, but that's a bit too complicated imho.
That said I would suggest using a regular map. That way you'll be able to pull your component by using ANY attribute, mapped to it, not a combination of all attributes:
Map<String, String> map = new HashMap<>()
map.put("attribute1", "component1")
map.put("attribute2", "component1")
map.put("attribute3", "component1")
map.put("attribute4", "component2")
map.put("attribute5", "component2")
map.put("attribute6", "component2")
and so on..
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Ivan Tovbin , Thank you for your help, just want to clarify, so if I have 150+ attribute, that would be equal to 150+ lines?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
In any case, even if you have a problem importing MultiKeyMap class, you can always use a regular Map, and simply use a collection of strings as its key, like so:
Map<List<String>, String> map = new HashMap<>()
map.put(Arrays.asList("attribute1", "attribute2", "attribute3"), "component1")
map.put(Arrays.asList("attribute3", "attribute4", "attribute5"), "component2")
But like I said, to get a value from this map you'll need to either provide the whole collection of attributes or create a logic that would first search for your attribute in all collections and then use the collection that contains it. For that i'd create a function:
Map<List<String>, String> map = new HashMap<>()
map.put(Arrays.asList("attribute1", "attribute2", "attribute3"), "component1")
map.put(Arrays.asList("attribute3", "attribute4", "attribute5"), "component2")
List<String> getAttributeList(String attribute, Map map){
List<String> attributeList = new ArrayList<>()
attributeList = map.keySet().find{it.contains(attribute)}
return attributeList
}
map.get(getAttributeList("attribute4", map))
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Ivan Tovbin, I tried to use your code but it returned an error
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Alvin You can ignore these errors. Scriptrunner's static type checking is not always accurate.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Ivan Tovbin, will do what you've suggest. will give feedback later. Thank you so much!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Charlie Misonne , I just want to ask something. How can I add Group , from Single Group picker field, based on the value of the Affected Service Field.
Scenario right now is whenever requestor selects "BOSS" as value of Affected Service Field, Component Field will be updated and Assignee will be the Component Lead.
Now I want to add Group too, based on the Value of Affected Service Field inside the condition. Can you help me? Thank you!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.