It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

ScriptRunner copy Epic custom field to every Epic child Edited

Hello everyone!

I need a help with the code for Script Runner.

I am trying to build the listener for the event of issue updated.

If the trigger issue is Epic I need to iterate through all issues in epic and copy custom field cf[10112] "Project team" to every issue in epic.

I know you can use 

def query = jqlQueryParser.parseQuery("\"Epic Link\" in (${issue.key})")

def results = searchProvider.search(query, user, PagerFilter.getUnlimitedFilter())

And then iterate with 

results.getIssues().each{
}

 But, unfortunately, I lack the knowledge in overall ScriptRunner code logic. 

Could you please help me with this task?

 

Thanks in advance! 

 

Update (working code for custom listener of "issue updated" event):

import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter

def issue = event.issue as Issue

if(issue.issueType.name=="Epic")
{
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser.class)
def searchProvider = ComponentAccessor.getComponent(SearchProvider.class)
def issueManager = ComponentAccessor.getIssueManager()
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

//def query = jqlQueryParser.parseQuery("\"Epic Link\" in (${issue.key})")
def query = jqlQueryParser.parseQuery("issue in childIssuesOf(\"${issue.key}\")")
def results = searchProvider.search(query, user, PagerFilter.getUnlimitedFilter())

log.error("List of epic children : " + results.getIssues())

def customFieldManager = ComponentAccessor.getCustomFieldManager()

int cfProjectTeamId = 10112
def cfProjectTeam = customFieldManager.getCustomFieldObject(cfProjectTeamId)
def cfProjectTeamValue = issue.getCustomFieldValue(cfProjectTeam)
log.error("Epic Project team : " + cfProjectTeamValue)

def changeHistoryManager = com.atlassian.jira.component.ComponentAccessor.getChangeHistoryManager()
results.getIssues().each {myIssue ->;
def docIssue = issueManager.getIssueObject(myIssue.id)
def cfProjectTeamCurrentValue = docIssue.getCustomFieldValue(cfProjectTeam)
log.error("Project team to be replaced : " + cfProjectTeamCurrentValue)
cfProjectTeam.updateValue(null, docIssue, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}
}

 

2 answers

1 accepted

0 votes
Answer accepted
Antoine Berry Community Leader Mar 27, 2019

Hi @Lev Ryvkin ,

You are absolutely right, please try and use this code : 

import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

// Your jql
def jqlSearch = ""\"Epic Link\" in (${issue.key})""
SearchService searchService = ComponentAccessor.getComponent(SearchService.class)
def userManager = ComponentAccessor.getUserManager()
IssueManager issueManager = ComponentAccessor.getIssueManager()
ApplicationUser user = userManager.getUserByKey('user_key')
List<Issue> issues = null
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)
if (parseResult.isValid()) {
def searchResult = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
issues = searchResult.issues.collect {issueManager.getIssueObject(it.id)}
} else {
log.error("Invalid JQL: " + jqlSearch);
}

def customFieldManager = ComponentAccessor.getCustomFieldManager()

int cfProjectTeamId = 10112
def cfProjectTeam = customFieldManager.getCustomFieldObject(cfProjectTeamId)
def cfProjectTeamValue = issue.getCustomFieldValue(cfProjectTeam)

def changeHistoryManager = com.atlassian.jira.component.ComponentAccessor.getChangeHistoryManager()
for (Issue issueLinked:issues){
def cfProjectTeamCurrentValue = issueLinked.getCustomFieldValue(cfOrgcfProjectTeam)
cfProjectTeam.updateValue(null, issueLinked, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}

 Just replace the user_key (pick a user that can access the issues).

Thanks! It looks almost what I need, but, unfortunately, it doesn't compile.

I've added

def issue = event.issue as Issue 

and running the whole code with 

if(issue.issueType.name=="Epic"){}

So the code is:

import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

def issue = event.issue as Issue

if(issue.issueType.name=="Epic") {
// Your jql
def jqlSearch = ("\"Epic Link\" in (${issue.key})")
SearchService searchService = ComponentAccessor.getComponent(SearchService.class)
def userManager = ComponentAccessor.getUserManager()
IssueManager issueManager = ComponentAccessor.getIssueManager()
ApplicationUser user = userManager.getUserByKey('user_key')
List<Issue> issues = null
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)
if (parseResult.isValid()) {
def searchResult = searchService.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
issues = searchResult.issues.collect {issueManager.getIssueObject(it.id)}
} else {
log.error("Invalid JQL: " + jqlSearch);
}

def customFieldManager = ComponentAccessor.getCustomFieldManager()

int cfProjectTeamId = 10112
def cfProjectTeam = customFieldManager.getCustomFieldObject(cfProjectTeamId)
def cfProjectTeamValue = issue.getCustomFieldValue(cfProjectTeam)

def changeHistoryManager = com.atlassian.jira.component.ComponentAccessor.getChangeHistoryManager()
for (Issue issueLinked:issues){
def cfProjectTeamCurrentValue = issueLinked.getCustomFieldValue(cfOrgcfProjectTeam)
cfProjectTeam.updateValue(null, issueLinked, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}
}

Attaching compile errors.image.pngimage.pngCapture.PNG 

Antoine Berry Community Leader Mar 27, 2019

Sorry, forgot to replace on variable. Change the last for loop for : 

for (Issue issueLinked:issues){
def cfProjectTeamCurrentValue = issueLinked.getCustomFieldValue(cfProjectTeam)
cfProjectTeam.updateValue(null, issueLinked, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}

 Do not worry about these errors. If you want to try once, go in "Script console", copy paste this code and run it. Just replace the jql for your test case : 

def jqlSearch = ""\"Epic Link\" in (ABC-123)""

Well, it seems to work, but doesn't update anything. How should we debug it?

As far as I found it doesn't even go to 

for (Issue issueLinked:issues){}

cycle as i added log.error(cfProjectTeamCurrentValue), which must output every project team list in every issue. And it does not at all.

But it works for epic.

int cfProjectTeamId = 10112
def cfProjectTeam = customFieldManager.getCustomFieldObject(cfProjectTeamId)
def cfProjectTeamValue = issue.getCustomFieldValue(cfProjectTeam)
log.error(cfProjectTeamValue)

actually returns Epic project team correctly.

Something is wrong with "for" cycle. 

Antoine Berry Community Leader Mar 27, 2019

I would suggest you update your for loop like this : 

for (Issue issueLinked:issues){
log.error("issueLinked : " + issueLinked)
def cfProjectTeamCurrentValue = issueLinked.getCustomFieldValue(cfProjectTeam)
log.error("cfProjectTeamCurrentValue : " + cfProjectTeamCurrentValue)
cfProjectTeam.updateValue(null, issueLinked, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}

Does it log something ? If not, it means your jql does not return anything, then check it with : 

log.error("jqlSearch : " + jqlSearch)

Logs inside cycle do not return anything.

log.error("jqlSearch : " + jqlSearch)

Returns

 image.pngBut the actual search returns 1 result 

Capture.PNG

Antoine Berry Community Leader Mar 27, 2019

It seems we need the jql is not parsed correctly in groovy then. Maybe try to replace the " with ' (double quotes by simple quotes) ?

Antoine Berry Community Leader Mar 27, 2019

I mean, around Epic Link.

It’s not the case. This line is suggested by Adaptivist vendor. 

Based on error I provided above I think <List> Of issues is not created at all.

there is an error in .collect method. 

I’ll come back to this later tomorrow. 

If you have anything more to adjust, you are more than welcome. I’ll try it tomorrow.

Thank you very much anyway! I think we are really close.

import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter

def issue = event.issue as Issue

if(issue.issueType.name=="Epic")
{
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser.class)
def searchProvider = ComponentAccessor.getComponent(SearchProvider.class)
def issueManager = ComponentAccessor.getIssueManager()
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

//def query = jqlQueryParser.parseQuery("\"Epic Link\" in (${issue.key})")
def query = jqlQueryParser.parseQuery("issue in childIssuesOf(\"${issue.key}\")")
def results = searchProvider.search(query, user, PagerFilter.getUnlimitedFilter())

log.error("List of epic children : " + results.getIssues())

def customFieldManager = ComponentAccessor.getCustomFieldManager()

int cfProjectTeamId = 10112
def cfProjectTeam = customFieldManager.getCustomFieldObject(cfProjectTeamId)
def cfProjectTeamValue = issue.getCustomFieldValue(cfProjectTeam)
log.error("Epic Project team : " + cfProjectTeamValue)

def changeHistoryManager = com.atlassian.jira.component.ComponentAccessor.getChangeHistoryManager()
results.getIssues().each {myIssue ->;
def docIssue = issueManager.getIssueObject(myIssue.id)
def cfProjectTeamCurrentValue = docIssue.getCustomFieldValue(cfProjectTeam)
log.error("Project team to be replaced : " + cfProjectTeamCurrentValue)
cfProjectTeam.updateValue(null, docIssue, new ModifiedValue(cfProjectTeamCurrentValue, cfProjectTeamValue), new DefaultIssueChangeHolder())
}
}

 The code that actually worked. The problem was in parser, which did not create a list of issues, so the update cycle was never executed. 

Now this works as intended. Thanks everyone who participated!

 

Best regards, 

Lev.

Like Antoine Berry likes this
Antoine Berry Community Leader Mar 28, 2019

Good job debugging this ! I have never had any issues with the code that I provided so I would not have guessed the parser was not working.

Have a great one.

Antoine

0 votes

Hi @Lev Ryvkin

oh boy, that is not a trivial task. But you seem to be on the right track.

What I'd try is:

1. get the custom field value of the epic

2. in the results.getIssues().each part, get the custom field for each issue and then change its value that you got from the epic's custom field

Does that make sense?

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted in Jira Core

How to manage many similar workflows?

I have multiple projects that use variations of the same base workflow. The variations depend on the requirements of the project or issue type. The variations mostly come in the form of new statuses ...

875 views 7 0
Join discussion

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you