Using scriptrunner to copy custom text field from parent to sub-task

Barbara Schnell April 2, 2019

Hi,

I am trying to have a custom text field called "Initiative" that exists in all issue types populated from Story to sub-task using scriptrunner.  I am new to scriptrunner and only have admin access to a dev server for exploration so may be unfamiliar with admin settings needed to make this work. 

Reference:   https://scriptrunner.adaptavist.com/5.4.47/jira/recipes/behaviours/subtask-default-fields.html

Problems: 

  1. I do not know how to do this: "The key to this is getting the parent issue ID via the parentIssueId form field. Once you have that you can load that issue, get the fields, and set them on the Create Issue dialog."  Parent IssueID is not a field I can add to the form.
  2. Should the script below work for text fields?  I have ready that I may need to replace "option" with "string" but am uncertain where to do this.

Script (I removed some error-causing items that I don't think I need and retained some standard fields to inherit values from parent):

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.fields.CustomField
//import com.atlassian.jira.web.util.OutlookDate
//import com.atlassian.jira.web.util.OutlookDateManager
import com.onresolve.jira.groovy.user.FieldBehaviours
import com.onresolve.jira.groovy.user.FormField
import groovy.transform.BaseScript

//import java.sql.Timestamp

import static com.atlassian.jira.issue.IssueFieldConstants.*

@BaseScript FieldBehaviours fieldBehaviours

FormField field = getFieldById(getFieldChanged())
FormField parent = getFieldById("parentIssueId")
Long parentIssueId = parent.getFormValue() as Long

if (!parentIssueId || field.getFormValue()) {
// this is not a subtask, or the field already has data
return
}

def issueManager = ComponentAccessor.getIssueManager()
def parentIssue = issueManager.getIssueObject(parentIssueId)
def customFieldManager = ComponentAccessor.getCustomFieldManager()

// REMOVE OR MODIFY THE SETTING OF THESE FIELDS AS NECESSARY
getFieldById(SUMMARY).setFormValue(parentIssue.summary)
//getFieldById(PRIORITY).setFormValue(parentIssue.getPriorityObject().id)

//OutlookDate outlookDate = ComponentAccessor.getComponent(OutlookDateManager).getOutlookDate(Locale.getDefault())
//getFieldById(DUE_DATE).setFormValue(outlookDate.formatDMY(parentIssue.getDueDate()))

//getFieldById(COMPONENTS).setFormValue(parentIssue.componentObjects*.id)
getFieldById(AFFECTED_VERSIONS).setFormValue(parentIssue.affectedVersions*.id)
getFieldById(FIX_FOR_VERSIONS).setFormValue(parentIssue.fixVersions*.id)
getFieldById(ASSIGNEE).setFormValue(parentIssue.assigneeId)
getFieldById(ENVIRONMENT).setFormValue(parentIssue.environment)
getFieldById(DESCRIPTION).setFormValue(parentIssue.description)
getFieldById(SECURITY).setFormValue(parentIssue.securityLevelId)
getFieldById(LABELS).setFormValue(parentIssue.labels)

// IF YOU DON'T WANT CUSTOM FIELDS COPIED REMOVE EVERYTHING BELOW HERE
// IF YOU ONLY WANT SOME FIELDS INHERITED ADD THEM TO THE LIST BELOW, OR LEAVE EMPTY FOR ALL
// eg ['Name of first custom field', 'Name of second custom field']
List copyCustomFields = ['Initiative']

List<CustomField> parentFields = customFieldManager.getCustomFieldObjects(parentIssue)

parentFields.each { cf ->
if (copyCustomFields && !copyCustomFields.contains(cf.name)) {
return
}

def parentValue = cf.getValue(parentIssue)

if (!parentValue) {
return
}

getFieldById(cf.id).setFormValue(parentValue)
log.debug("parentValue: ${parentValue?.class} for type ${cf.name}")

// if (parentValue instanceof Timestamp) {
// getFieldById(cf.id).setFormValue(outlookDate.formatDMY(parentValue))
// } else
if (parentValue instanceof Option) {
getFieldById(cf.id).setFormValue(parentValue.optionId)
} else if (parentValue instanceof List<Option>) {
parentValue = parentValue as List<Option> //This cast is just to placate the static type checker
getFieldById(cf.id).setFormValue(parentValue.collect { it.optionId })
} else {
getFieldById(cf.id).setFormValue(parentValue)
}
}

 

 

4 answers

0 votes
wajdan zahid November 25, 2021

Hi @Antoine Berry 

looking at all the help you have been provided couldn't resist asking one for myself :D 

Thank you first of all.

Can the same thing work for cloning issue in post function script?

I want to add multiple custom fields into description of the issue being cloned.

I am new to scriptrunner and cannot find a good place to look for this feature. 

Thank you in advance

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
November 26, 2021

Hello @wajdan zahid ,

I would advise you create a new question for a thorough answer (you can ping me there), this one is getting messy. It is possible to copy field values to another issue. I am not sure if there is a way to detect that the issue is being cloned upon creation, and I would need to do some testing.

0 votes
Alan October 30, 2019

Hey Antoine:

Tried your script above (to copy custom field value from parent) and got this:

 

image.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
October 31, 2019

Hi @Alan ,

Try executing as is, these are just warnings.

Sebastian Gomez November 20, 2019

This post was very helpful thank you! @Barbara Schnell @Antoine Berry 

Like Antoine Berry likes this
Laci August 25, 2021

@Antoine Berry How difficult would something like this be to implement based on linked issues? So if I have stories that are derived from features (a level my team added  into our hierarchy) I would like a text field flowed from my feature to my story.

Thanks!

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
August 30, 2021

Hi @Laci ,

It would be possible, but you would have to go through some extra steps, since each issue can be linked to multiple issues, with different link types. Here is a snippet that you can use to retrieve the linked issues and use the same logic as above : 

 

import com.atlassian.jira.issue.link.IssueLink;
import com.atlassian.jira.component.ComponentAccessor;

def allOutIssues = [];
List<IssueLink> allOutIssueLink = ComponentAccessor.getIssueLinkManager().getOutwardLinks(issue.getId());
for (Iterator<IssueLink> outIterator = allOutIssueLink.iterator(); outIterator.hasNext();) {
IssueLink issueLink = (IssueLink) outIterator.next();
allOutIssues << issueLink.getDestinationObject();
}


def allInIssues = [];
List<IssueLink> allInIssueLink = ComponentAccessor.getIssueLinkManager().getInwardLinks(issue.getId());
for (Iterator<IssueLink> outIterator = allInIssueLink.iterator(); outIterator.hasNext();) {
IssueLink issueLink = (IssueLink) outIterator.next();
allOutIssues << issueLink.getDestinationObject();
}

Antoine

0 votes
Barbara Schnell April 4, 2019

I thought it would be good to summarize what finally worked here:

Goals:

  1. Have a story, bug, task, etc. inherit a custom field value from epic upon create.
  2. Have a sub-task inherit a custom field value from a parent task upon create.

Goal 1 Post Function (Copy custom field from Epic to Story upon create):

  1. Create custom post function using script-runner on the Create transition in your workflow.
  2. Script should precede the out-of-the-box "Create the issue originally" post function
  3. Script:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue


def customFieldManager = ComponentAccessor.getCustomFieldManager()

def issue = issue as MutableIssue

 

["Initiative"].each { cfname ->

def cf = customFieldManager.getCustomFieldObjects(issue).find {it.name == cfname}
log.error("cf : " + cf)
log.error("issue : " + issue)

def epicCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'}

if (!cf || !epicCf) {return}

def epic = issue.getCustomFieldValue(epicCf) as Issue


if (!epic) {return}

def cfValue = cf.getValue(epic)
log.error("cfValue : " + cfValue)

issue.setCustomFieldValue(cf, cfValue)

}

4. Screen capture: Screenshot 2019-04-04 09.29.07.png

Goal 2 Post Function (Copy custom field from Story to sub-task upon create):

  1. Create custom post function using script-runner on the Create transition in your workflow.
  2. Script should FOLLOW the out-of-the-box "Create the issue originally" post function
  3. Script:

mport com.atlassian.jira.issue.util.DefaultIssueChangeHolder

import com.atlassian.jira.issue.ModifiedValue

import com.atlassian.jira.component.ComponentAccessor

import com.atlassian.jira.issue.index.IssueIndexingService

 

 

def customFieldManager = ComponentAccessor.getCustomFieldManager()

 

int cfId = 11002

def cf = customFieldManager.getCustomFieldObject(cfId)

log.error("cf : " + cf)

log.error("issue : " + issue)

def cfValue = issue.getCustomFieldValue(cf)

log.error("cfValue : " + cfValue)

def cfParentValue = issue.getParentObject().getCustomFieldValue(cf)

log.error("cfParentValue : " + cfParentValue)

def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)

issue.store()

issueIndexingService.reIndex(issue)

 

 

if (issue.isSubTask()){

cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())

log.error("Custom field updated on issue " + issue.getKey() + " with value " + cfParentValue)

}

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 5, 2019

Hi Barbara, 

As for your first script, I would advise to update it from setCustomField(cf... method to cf.updateValue(... because as you can see here setCustomField does not write in the database. Although in that case, since you have a re-index postfunction, it is working.

You can also remove 

def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
issue.store()
issueIndexingService.reIndex(issue)

 in the second script I think (or place it after the update).

Interesting to see these two different approachs anyway. :)

Barbara Schnell April 5, 2019

Hi Antoine,

I think I will leave the post  functions separate for now since the epic one did not seem to work when I moved to follow the original issue create.  I would like your feedback on how to modify the following so it writes to the database though. I'm not exactly sure where to change "setCustomField(cf... method to cf.updateValue(... ":

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue


def customFieldManager = ComponentAccessor.getCustomFieldManager()

def issue = issue as MutableIssue

 

["Initiative"].each { cfname ->

def cf = customFieldManager.getCustomFieldObjects(issue).find {it.name == cfname}
log.error("cf : " + cf)
log.error("issue : " + issue)

def epicCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'}

if (!cf || !epicCf) {return}

def epic = issue.getCustomFieldValue(epicCf) as Issue


if (!epic) {return}

def cfValue = cf.getValue(epic)
log.error("cfValue : " + cfValue)

issue.setCustomFieldValue(cf, cfValue)

}

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 5, 2019

Hi, 

Try this script : 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue


def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue

def cf = customFieldManager.getCustomFieldObjects(issue).find {it.name == "Initiative"}
def epicCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'}

if (!cf || !epicCf) {return}

def epic = issue.getCustomFieldValue(epicCf) as Issue

if (!epic) {return}

def cfEpicValue = epic.getCustomFieldValue(cf)
def cfCurrentValue = issue.getCustomFieldValue(cf)

cf.updateValue(null, issue, new ModifiedValue(cfCurrentValue, cfValue), new DefaultIssueChangeHolder())
issue.setCustomFieldValue(cf, cfValue)

Here you are retrieving the fields by name, i'd suggest using the ID but it is up to you.

Also you can remove the logs from the other script if you don't want to flood.

Antoine

Barbara Schnell April 5, 2019

I get an error that the cf value is undeclared:

 

Screenshot 2019-04-05 08.29.35.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 5, 2019

Sorry, sloppy of me. Use 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue


def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue

def cf = customFieldManager.getCustomFieldObjects(issue).find {it.name == "Initiative"}
def epicCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'}

if (!cf || !epicCf) {return}

def epic = issue.getCustomFieldValue(epicCf) as Issue

if (!epic) {return}

def cfEpicValue = epic.getCustomFieldValue(cf)
def cfCurrentValue = issue.getCustomFieldValue(cf)

cf.updateValue(null, issue, new ModifiedValue(cfCurrentValue, cfEpicValue), new DefaultIssueChangeHolder())
Barbara Schnell April 5, 2019

How did we fix this again?

2019-04-05 15:13:40,552 ERROR [workflow.ScriptWorkflowFunction]: cf : Initiative
2019-04-05 15:13:40,560 ERROR [workflow.ScriptWorkflowFunction]: issue : test new code with logging
2019-04-05 15:13:40,609 ERROR [workflow.ScriptWorkflowFunction]: Custom field updated on issue null with value TP3-1 Initiative without Portfolio
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 5, 2019

Just place the postfunction after the "Create issue orginally" postfunction. :)

Barbara Schnell April 5, 2019

Yes!  Good to go.  Thanks Antoine!  

Like Antoine Berry likes this
Barbara Schnell April 11, 2019

I Antoine - I am trying to do the same thing with labels now.  Here is my code:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.jira.issue.label.Label

//variables for issue re-index after changing the value
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
boolean wasIndexing = ImportUtils.isIndexIssues();

// Get Custom Field as string (Epic Link)
def epicLinkCf = customFieldManager.getCustomFieldObjectByName("Epic Link")

// Get Custom Field Value as String
CustomField epicLink = customFieldManager.getCustomFieldObjectByName('Epic Link');
String EpicName = issue.getCustomFieldValue(epicLink);

if(EpicName){

// Get Epic from Issue
def epicIssue = issue.getCustomFieldValue(epicLinkCf) as Issue

 

// Get Labels from Epic
def currentValue = epicIssue.getLabels()

// Set Label to Story
issue.setLabels(currentValue);
}

 

And here's what the log says:

2019-04-10 18:25:17,196 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2019-04-10 18:25:17,208 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: TP3-22, actionId: 1, file: <inline script>
groovy.lang.MissingPropertyException: No such property: customFieldManager for class: Script536
 at Script536.run(Script536.groovy:17)

Maybe because I'm not familiar with all of the jira functions and methods here so if you could help me with this one then recommend a class or book I would greatly appreciate it!

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

Hi @Barbara Schnell ,

As you can see in the logs you variable customFieldManager does not exist. If you look in the previous script you have an example : 

def customFieldManager = ComponentAccessor.getCustomFieldManager()

You have a lot of resources online. You can start here for ScriptRunner. Otherwise if you need (paid) classes I guess you could check the atlassian portal ?

Barbara Schnell April 12, 2019

Thanks - I think I am close with this label inherit script but am still getting errors. The inherit cf Initiative post function precedes this script after original issue create: 

Script:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.jira.issue.label.Label

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue

log.error("issue : " + issue)
//variables for issue re-index after changing the value
//def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
//boolean wasIndexing = ImportUtils.isIndexIssues();

// Get Custom Field as string (Epic Link)
def epicLinkCf = customFieldManager.getCustomFieldObjectByName("Epic Link")


// Get Custom Field Value as String
CustomField epicLink = customFieldManager.getCustomFieldObjectByName('Epic Link');
String EpicName = issue.getCustomFieldValue(epicLink);

if(EpicName){

// Get Epic from Issue
def epicIssue = issue.getCustomFieldValue(epicLinkCf) as Issue
log.error ("epicIssue : " +epicIssue)


// Get Labels from Epic
def epicLabel = epicIssue.getLabels()

log.error("epicLabel : " + epicLabel)

// Set Label to Story
issue.setLabels(epicLabel);
}

 

 

Errors:

2019-04-12 12:31:29,528 ERROR [workflow.ScriptWorkflowFunction]: issue : TP3-29
2019-04-12 12:31:29,530 ERROR [workflow.ScriptWorkflowFunction]: epicIssue : TP3-2
2019-04-12 12:31:29,531 ERROR [workflow.ScriptWorkflowFunction]: epicLabel : [label1, label2]
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

Well those are not real errors, it appears as "ERROR" because you loggued that way : 

log.error(.....)

You could use log.debug or log.info instead depending on how your log level is configured...

These logs actually look fine, does that not work ?

Barbara Schnell April 12, 2019

That's what I figured but the label values are not getting set on the story from the epic.  The script seems to complete successfully but the labels field is empty.

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

I think the issue comes from

issue.setLabels

Try using LabelManager object like in this topic.

It's the same as when I did not recommend 

issue.setCustomfield 

:)

Antoine

Barbara Schnell April 12, 2019

I guess this is over my head. I'm not sure I'm following you.  And why I can't use issue.setLabels.

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

I don't know, actually there is nothing in the doc, so maybe it should work correctly. Documentation only states that setCustomfieldValue method does not write values in the database.

Try to use this instead : 

LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
labelManager.setLabels(currentUser, issue.getId(), epicLabel, false, false)

Antoine

Barbara Schnell April 12, 2019

I get this error.

Screenshot 2019-04-12 07.47.21.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

Please provide the whole message, it is truncated.

Barbara Schnell April 12, 2019

How do I do that?

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 12, 2019

Have you actually tried to save as is ? Groovy console will often preview errors, which are not actually errors in groovy because object types can be implicit.

Antoine

Barbara Schnell April 12, 2019

I did and was able to save.  

Here are the errors:

Time (on server): Fri Apr 12 2019 10:31:11 GMT-0600 (Mountain Daylight Time)

The following log information was produced by this execution. Use statements like:log.info("...") to record logging information.

2019-04-12 16:31:11,241 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2019-04-12 16:31:11,244 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: TP3-33, actionId: 1, file: <inline script>
java.lang.ClassCastException: com.atlassian.jira.issue.label.Label cannot be cast to java.lang.String
 at com.atlassian.jira.issue.label.DefaultLabelManager.validateLabels(DefaultLabelManager.java:267)
 at com.atlassian.jira.issue.label.DefaultLabelManager.setLabels(DefaultLabelManager.java:60)
 at com.atlassian.jira.issue.label.LabelManager$setLabels$0.call(Unknown Source)
 at Script701.run(Script701.groovy:47)
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 15, 2019

Hi,

As you can see the setLabels method is expecting Strings instead of Labels. Give this a try : 

LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
if (epicLabel != null && epicLabel.size()){
def labels = []
for (Label label:epicLabel){
labels.add(label.getLabel())
}
labelManager.setLabels(currentUser, issue.getId(), labels, false, false)
}

without forgetting this import : 

import com.atlassian.jira.issue.label.Label

I did not try it but this is my best guess.

Antoine

Barbara Schnell April 15, 2019

Groovy didn't like the nested expression.  See attached and this link:

https://github.com/fabric8io/fabric8/issues/2707

error:

The script could not be compiled: <pre>org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script730.groovy: 54: expecting '}', found '' @ line 54, column 2. } ^ 1 error </pre>.Screenshot 2019-04-15 06.57.58.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 15, 2019

It was sloppy of me, I forgot something, sorry...

if (epicLabel != null && epicLabel.size() > 0){

Antoine 

Barbara Schnell April 15, 2019

No problem at all.  I appreciate the help.  However still getting the same error.Screenshot 2019-04-15 07.07.45.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 15, 2019

Could you please copy the whole script so I can more easily check what is wrong ?

Thanks.

Barbara Schnell April 15, 2019

Sure:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.jira.issue.label.Label

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue

log.info("issue : " + issue)
//variables for issue re-index after changing the value
//def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
//boolean wasIndexing = ImportUtils.isIndexIssues();

// Get Custom Field as string (Epic Link)
def epicLinkCf = customFieldManager.getCustomFieldObjectByName("Epic Link")


// Get Custom Field Value as String
CustomField epicLink = customFieldManager.getCustomFieldObjectByName('Epic Link');
String EpicName = issue.getCustomFieldValue(epicLink);
//LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def labels = labelManager.getLabels(issue.id).collect{it.getLabel()}
if(EpicName){

// Get Epic from Issue
def epicIssue = issue.getCustomFieldValue(epicLinkCf) as Issue
log.info ("epicIssue : " +epicIssue)


// Get Labels from Epic
def epicLabel = epicIssue.getLabels()

log.info("epicLabel : " + epicLabel)

// Set Label to Story
// issue.setLabels(epicLabel);
LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
if (epicLabel != null && epicLabel.size() > 0){
def labels = []
for (Label label:epicLabel){
labels.add(label.getLabel())
}
labelManager.setLabels(currentUser, issue.getId(), labels, false, false)
}

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 15, 2019

As expected you are missing a bracket at the end, please try : 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.jira.issue.label.Label
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue
log.info("issue : " + issue)
//variables for issue re-index after changing the value
//def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
//boolean wasIndexing = ImportUtils.isIndexIssues();
// Get Custom Field as string (Epic Link)
def epicLinkCf = customFieldManager.getCustomFieldObjectByName("Epic Link")

// Get Custom Field Value as String
CustomField epicLink = customFieldManager.getCustomFieldObjectByName('Epic Link');
String EpicName = issue.getCustomFieldValue(epicLink);
//LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def labels = labelManager.getLabels(issue.id).collect{it.getLabel()}
if(EpicName){
// Get Epic from Issue
def epicIssue = issue.getCustomFieldValue(epicLinkCf) as Issue
log.info ("epicIssue : " +epicIssue)

// Get Labels from Epic
def epicLabel = epicIssue.getLabels()
log.info("epicLabel : " + epicLabel)

// Set Label to Story
// issue.setLabels(epicLabel);
LabelManager labelManager = ComponentAccessor.getComponent(LabelManager)
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
if (epicLabel != null && epicLabel.size() > 0){
def labels = []
for (Label label:epicLabel){
labels.add(label.getLabel())
}
labelManager.setLabels(currentUser, issue.getId(), labels, false, false)
}
}
Barbara Schnell April 15, 2019

That worked!

 

Except I needed to modify the last part to this based on my version of jira:

 

labelManager.setLabels(currentUser,issue.id,labels.toSet(),false,false) 

 

I really appreciate your help on these!

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 15, 2019

Nice, good job fixing that. What version are you using ?

0 votes
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 3, 2019

Hi, 

Are ou trying to do this in behaviours, or in a postfunction / script listener ?

For the latter, you can use this snippet to copy the field value of the parent issue : 

import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.component.ComponentAccessor


//Your custom field ID
int cfId = 10100
def cf = customFieldManager.getCustomFieldObject(cfId)
def cfValue = issue.getCustomFieldValue(cf)
def cfParentValue = issue.getParentObject().getCustomFieldValue(cf)

cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())

 Antoine

Barbara Schnell April 3, 2019

I am using a post function.  Is the above the entirety of the code I would need? Or are you saying to add this to mine above?

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 3, 2019

Your code looks like a behaviours script, which would not work in a post function.

The code above will update the custom field (id 10100) with the value of this field on the parent issue. So make sure the post function is on a sub-task workflow only or use this condition to check that the issue is a sub-task : 

if (issue.isSubTask()){ ...

Antoine

Barbara Schnell April 3, 2019

ok, I used the approach of hovering over the custom field to get the cfid (mentioned here https://community.atlassian.com/t5/Jira-questions/How-can-I-find-the-ID-of-a-custom-field-in-Jira-5/qaq-p/272632

This is the post function now and it still does not work.  Do I need a separate workflow for sub-task?

import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.component.ComponentAccessor



int cfId = 1102
def cf = customFieldManager.getCustomFieldObject(cfId)
def cfValue = issue.getCustomFieldValue(cf)
def cfParentValue = issue.getParentObject().getCustomFieldValue(cf)

if (issue.isSubTask()){
cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())}

Barbara Schnell April 3, 2019

I should mention I have another post function after this one on Create that updates the Initiative on issue from the Epic based on epic link. This works fine:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue

 

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issue = issue as MutableIssue


["Initiative"].each { cfname ->
def cf = customFieldManager.getCustomFieldObjects(issue).find {it.name == cfname}
def epicCf = customFieldManager.getCustomFieldObjects(issue).find {it.name == 'Epic Link'}
if (!cf || !epicCf) {return}

def epic = issue.getCustomFieldValue(epicCf) as Issue
if (!epic) {return}

def cfValue = cf.getValue(epic)

issue.setCustomFieldValue(cf, cfValue)
}

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Hi Barbara,

I think there is a typo in your custom field id because it cannot be 4 digits only. Go to Custom fields > Configure 

image.pngimage.png

As for your script, I usually encourage to use custom field ID instead of name (so you are fine if any admin changes the name). But of course it is up to you. 

However I strongly recommand using 

cf.updateValue(...

instead of 

issue.setCustomFieldValue(cf, cfValue)

because as you can see here https://docs.atlassian.com/software/jira/docs/api/7.1.2/com/atlassian/jira/issue/IssueImpl.html setCustomFieldValue does not write the value in the database.

Antoine    

Barbara Schnell April 4, 2019

Thanks - you were right - I was missing a zero.  I made the change and still no luck however.

import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.component.ComponentAccessor



int cfId = 11002
def cf = customFieldManager.getCustomFieldObject(cfId)
def cfValue = issue.getCustomFieldValue(cf)
def cfParentValue = issue.getParentObject().getCustomFieldValue(cf)

if (issue.isSubTask()){
cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())}

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Can you check the logs to help investigate ?

Barbara Schnell April 4, 2019

Time (on server): Thu Apr 04 2019 07:47:35 GMT-0600 (Mountain Daylight Time)

The following log information was produced by this execution. Use statements like:log.info("...") to record logging information.

2019-04-04 13:47:35,841 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2019-04-04 13:47:35,858 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: null, actionId: 1, file: <inline script>
groovy.lang.MissingPropertyException: No such property: customFieldManager for class: Script239
 at Script239.run(Script239.groovy:8)
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Sorry, forgot to add the customFieldManager definition. Add this line after the imports : 

def customFieldManager = ComponentAccessor.getCustomFieldManager()
Barbara Schnell April 4, 2019

ok, and now this:

 

Time (on server): Thu Apr 04 2019 07:54:08 GMT-0600 (Mountain Daylight Time)

The following log information was produced by this execution. Use statements like:log.info("...") to record logging information.

2019-04-04 13:54:08,702 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2019-04-04 13:54:08,708 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: null, actionId: 1, file: <inline script>
java.lang.NullPointerException
 at com.atlassian.jira.issue.IssueImpl.getCustomFieldValue(IssueImpl.java:898)
 at com.atlassian.jira.issue.Issue$getCustomFieldValue$0.call(Unknown Source)
 at Script243.run(Script243.groovy:10)
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Thanks for providing the logs. Can you update your script as : 

import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.component.ComponentAccessor

def customFieldManager = ComponentAccessor.getCustomFieldManager()

int cfId = 11002
def cf = customFieldManager.getCustomFieldObject(cfId)
log.error("cf : " + cf)
log.error("issue : " + issue)
def cfValue = issue.getCustomFieldValue(cf)
log.error("cfValue : " + cfValue)
def cfParentValue = issue.getParentObject().getCustomFieldValue(cf)
log.error("cfParentValue : " + cfParentValue)

if (issue.isSubTask()){
cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())}

It will make easier keeping track of the variables used in there. Execute your script and please provide the logs again :)

Like Sebastian Gomez likes this
Barbara Schnell April 4, 2019

ok, here are the logs with the script from above:

 

Time (on server): Thu Apr 04 2019 08:12:56 GMT-0600 (Mountain Daylight Time)

The following log information was produced by this execution. Use statements like:log.info("...") to record logging information.

2019-04-04 14:12:56,054 ERROR [workflow.ScriptWorkflowFunction]: cf : Initiative
2019-04-04 14:12:56,058 ERROR [workflow.ScriptWorkflowFunction]: issue : test 11002 - test 3
2019-04-04 14:12:56,058 ERROR [workflow.ScriptWorkflowFunction]: cfValue : null
2019-04-04 14:12:56,061 ERROR [workflow.ScriptWorkflowFunction]: cfParentValue : TP-2 Initiative 1
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Did it work this time ? there is no error message.

Barbara Schnell April 4, 2019

The script completed successfully but the field did not get populated.  Errors posted above seem to point to the workflow.

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Could you add a log in the next line as well so we can make sure the script executed correctly ? 

if (issue.isSubTask()){
cf.updateValue(null, issue, new ModifiedValue(cfValue, cfParentValue), new DefaultIssueChangeHolder())
log.error("Custom field updated on issue " + issue.getKey() + " with value " + cfParentValue)
}
Barbara Schnell April 4, 2019

ok - now it seems like it thinks it's updating the field but since the issue is null, it's not. Why is the issue null in the error below?

 

Time (on server): Thu Apr 04 2019 08:31:55 GMT-0600 (Mountain Daylight Time)

The following log information was produced by this execution. Use statements like:log.info("...") to record logging information.

2019-04-04 14:31:55,112 ERROR [workflow.ScriptWorkflowFunction]: cf : Initiative
2019-04-04 14:31:55,112 ERROR [workflow.ScriptWorkflowFunction]: issue : test initiative 3
2019-04-04 14:31:55,113 ERROR [workflow.ScriptWorkflowFunction]: cfValue : null
2019-04-04 14:31:55,114 ERROR [workflow.ScriptWorkflowFunction]: cfParentValue : TP-2 Initiative 1
2019-04-04 14:31:55,123 ERROR [workflow.ScriptWorkflowFunction]: Custom field updated on issue null with value TP-2 Initiative 1

   

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Well it seems that your issue does not have a key (I have no idea why). The issue object is not null since it is printed correctly.

Are you completely sure it did not update (is the custom field on the view screen?) ? You could quickly check in a filter by adding the custom field as a column.

If it did not update...

Is "test initiative 3" the summary of the issue ? 

Could you screenshot your issue view screen ?

How are you triggering your post function ?

Barbara Schnell April 4, 2019

Yes, I'm sure it is not updated (from jql and edit issue).  The issue getting updated is a sub-task.  Does jira see a sub-task as just another issue? Screenshot 2019-04-04 09.04.33.pngScreenshot 2019-04-04 09.05.55.pngScreenshot 2019-04-04 09.07.33.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Absolutely. From a groovy point of view there is no difference. 

Try adding this import 

import com.atlassian.jira.issue.index.IssueIndexingService

And these lines at the end : 

def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
issue.store()
issueIndexingService.reIndex(issue)

Also could you provide a screenshot of the transition post-functions ?

Barbara Schnell April 4, 2019

I am triggering the post function at the Create transition as a custom script using scriptrunner.

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Ok, then it is most likely because the script post-function is not the last executed. Please place it at the last spot (especially after "Creates the issue originally").

Barbara Schnell April 4, 2019

When I added the line above I got this error:

Screenshot 2019-04-04 09.15.17.png

Screenshot of transition post functions here:

Screenshot 2019-04-04 09.16.38.png

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Yes you definitely need to place the script post function after the "Create the issue originally". Then you probably do not need the store and reindex part in the script.

Like # people like this
Barbara Schnell April 4, 2019

That did it! Thanks so much for your help on this!  What is interesting here is that I did not need to do that for the story to inherit the custom field from the epic.  Can you help me understand why?

Like # people like this
Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 4, 2019

Glad to help ! Please mark the answer as accepted so people with the same issue are lead on this topic. :)

The problem was that the sub-task was not "fully" created at execution time, so we could not update the value on a object that does not exist yet. 

I guess your first script copies the Initiative from the Epic to the Story ? And this sub-task is the child of the Story ? In that case the Epic and Story are already created so the sub-task creation happening afterwards has no impact. :)

Barbara Schnell April 4, 2019

disregard

Barbara Schnell April 4, 2019

Is it possible to combine these 2 post functions?  if sub-task... else ...?  If not - I am stuck again unless I have separate workflows for sub-task vs others.  

Antoine Berry
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 5, 2019

Yes absolutely, you can test against the issue type. Are you still stuck ?

Suggest an answer

Log in or Sign up to answer