Hey folks,
I'm spinning trying to get a scriptrunner script to work (either on Post Function or Automation). This script will process/fire when an "Automation for Jira" job picks an issue up and creates a linked issue. Essentially, the script consists of two parts:
From the new linked issue, get all issue links. Return with the Key of the issue links of a particular type. At this point it should only be 1 (the trigger issue) as it was just created. I have been working with something like this:
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
IssueManager im = ComponentAccessor.getIssueManager()
MutableIssue issue = im.getIssueObject("ADEMO-11") //TODO: Get current issue instead of static issue
// For later, to get the issue its on: def issue = ComponentAccessor.issueManager.getIssueByCurrentKey(issueKey)
def issueService = ComponentAccessor.issueService
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def links = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId())
def keys = ""
for( l in links) {
keys = keys + l.getDestinationObject()
}
return keys
This is great, and in the future I will be updating issue to point to the current issue.
A switch or if statement to compare the linked issue keys with Regex. For the sake of testing right now, I'm just using a text field:
if(issue){
//get the value
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject("customfield_18701")
def cFieldValue = issue.getCustomFieldValue(cField)
//update the value
def changeHolder = new DefaultIssueChangeHolder()
cField.updateValue(null, issue, new ModifiedValue(cFieldValue, "Testing if this works!"),changeHolder)
//TODO: Configure to use Cascading select list
}else {
return "Nothing is linked"
}
With this part, i'm able to pass a string. However, I really want to be passing what I'm returning as `keys` in part 1. I feel like I'm missing something very obvious.
You are overcomplicating things I think ...
Let me check if I understand your requirement.. you want to select an option in a cascading select that contains a list of Project Keys. The option to select will be determined by an issue link of a specific type.
What happens if you have more than one link of that same type?
Do you only need to select the first field in the cascading select from the linked issue? What's the second field based on?
Here is some sample code to get the project key (no regex required) from the linked issue and how you can set that value to the cascading select field.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
def im = ComponentAccessor.issueManager
def lm = ComponentAccessor.issueLinkManager
def cfm = ComponentAccessor.customFieldManager
def om = ComponentAccessor.optionsManager
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issue = im.getIssueObject('ADEMO-11') //in a workflow postfunction, you can just comment out this line, the script binding already includes an issue variable
def issueLinks = lm.getOutwardLinks(issue.id)
def linkType = 'Cloners' //specify the link type name you want to filter your issue links with
def issueLinksOfCorrectType = issueLinks.findAll{it.issueLinkType.name == linkType}
//issueLink object contain a destinationObject of type issue, you can ket the key specifically from that issue object
def listOfLinkedIssueKeys = issueLinksOfCorrectType.collect{it.destinationObject.key}
//and further, you can drill into the issue object's project object and get the project Key
def listOfProjectKeys = issueLinksOfCorrectType.collect{it.destinationObject.projectObject.key}
def cascadingSelectCfId = 18702 //or whatever number
def csFieldCf = cfm.getCustomFieldObject(cascadingSelectCfId)
def config = csFieldCf.getRelevantConfig(issue) //this is the configuration that contains all your options
def options = om.getOptions(config)
//getting the option that corresponds to the project of the first linked issue
def projectKeyOption = options.find{it.value == listOfProjectKeys[0]}
//change the issue object (in memory only)
issue.setCustomFieldValue(csFieldCf, [null:projectKeyOption])
//if you know the "child option" to select, it would look like this
//issue.setCustomFieldValue(csFieldCf, [null:projectKeyOption, '1':childOption])
//this saves the changed issue to the db
im.updateIssue(currentUser,issue, EventDispatchOption.DO_NOT_DISPATCH,false)
//now we have to re-index the issue, this doesn't happen automatically after you update
def wasIndexing = ImportUtils.indexIssues
ImportUtils.indexIssues = true
ComponentAccessor.getComponent(IssueIndexingService).reIndex(issue)
ImportUtils.indexIssues = wasIndexing
Note that the "cField.updateValue()" method is rarely indicated or recommended. My preference is to use the issueManager.updateIssue() method and manually re-index.
You could alternatively also use the issueService
def is = ComponentAccessor.issueService
def iip = is.newIssueInputParameters()
iip.addCustomFieldValue(csFieldCf.id, projectKeyOption.optionId.toString())
//if you know the child option
iip.addCustomFieldValue(csFieldCf.id + ':1', projectKeyOption.optionId.toString())
def validateResult = is.validateUpdate(currentUser, issue.id, iip)
if (validateResult.valid) {
//this will update the issue and re-index it
is.update(currentUser, validateResult, EventDispatchOption.DO_NOT_DISPATCH, false)
}
Thanks Peter, this is super helpful. It's very close, but I think I didn't lay out my use case well enough. Essentially, here's what we're doing:
For example, if the BAR team were to have an issue sent over to the FOO team, we would do something like:
...
if (linkedIssueKeys =~BAR^){
issue.setCustomFieldValue(csFieldCf, [Marketing:Bar Team]
}
else if (linkedIssueKeys =~GIT^){
issue.setCustomFieldValue(csFieldCf, [Engineering:Red Team]
}
...
Or something of the like. Essentially, we just want to identify if there is a linked issue at the time of creation AND use that linked issue's key to set the value(s) based off a pre-defined statement.
Hope that helps, I appreciate the examples and insight so far.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I would use a map object rather than a series of if-else or switch.
Try something like this:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
def im = ComponentAccessor.issueManager
def is = ComponentAccessor.issueService
def lm = ComponentAccessor.issueLinkManager
def cfm = ComponentAccessor.customFieldManager
def om = ComponentAccessor.optionsManager
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def projectTeamMap = [
BAR: [dept: 'Marketing', team: 'Bar Team'],
GIT: [dept: 'Engineering', team: 'Red Team'],
// repeat as needed
]
def linkType = 'Cloners' //specify the link type name you want to filter your issue links with
def cascadingSelectCfId = 18702 //or whatever number
def issue = im.getIssueObject('ADEMO-11') //in a workflow postfunction, you can just comment out this line, the script binding already includes an issue variable
def issueLinks = lm.getOutwardLinks(issue.id)
def issueLinksOfCorrectType = issueLinks.findAll { it.issueLinkType.name == linkType }
def listOfProjectKeys = issueLinksOfCorrectType.collect { it.destinationObject.projectObject.key }
assert listOfProjectKeys: "No linked issue of type $linkType were found"
def projectKey = listOfProjectKeys.first()
def csFieldCf = cfm.getCustomFieldObject(cascadingSelectCfId)
def config = csFieldCf.getRelevantConfig(issue)
def options = om.getOptions(config)
//here we get the department and team from the map using the project key
def department = projectTeamMap[projectKey].dept
def team = projectTeamMap[projectKey].team
assert team && department : "$projectKey is not defined in projectTeamMap: ${projectTeamMap.keySet()}"
def departmentOption = options.find{it.value == department}
def teamOption = departmentOption.childOptions.find{it.value == team}
def iip = is.newIssueInputParameters()
iip.addCustomFieldValue(csFieldCf.id, departmentOption.optionId.toString())
iip.addCustomFieldValue(csFieldCf.id + ':1', teamOption.optionId.toString())
def validateResult = is.validateUpdate(currentUser, issue.id, iip)
assert validateResult.valid : validateResult.errorCollection
def updateResult = is.update(currentUser, validateResult, EventDispatchOption.DO_NOT_DISPATCH, false)
assert updateResult.valid : updateResult.errorCollection
I added some power assertion to help catch error cases.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
A map definitely makes more sense, and that appears to have done the trick. The only updates I needed to make were to fit my use case of using Inward links instead of outward.
EDIT: Unfortunately I now understand why you were using outward instead of inward. Unfortunately, although the link and link type appears to exist, it fails when collecting with the following error:
java.lang.AssertionError: No linked issue of type Relates were found. Expression: listOfProjectKeys. Values: listOfProjectKeys = [] at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:417) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:670) at Script1645.run(Script1645.groovy:27) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155) at java.scripting/javax.script.AbstractScriptEngine.eval(Unknown Source)
As you can see below, there is at least one link present on my test issue and I have updated the link type to "Relates" to reflect this:
For some reason, getting these links in a meaningful way is my kryptonite. Sincerely appreciate all the help so far!
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.