Post-function to clone an issue multiple times based on issue's custom field value

Ivan Tovbin Community Champion Dec 15, 2017

Hello,

An issue has a multi-select custom field (checkboxes) with at least one selected option.

I'm trying to create a script for a workflow post-function that should do the following.


The easy part:

If the custom field has only one option selected, simply set issue type depending on the selected option and assign the issue to current user.


The tricky part (the one I could use some help with):

If the custom field has more than one option selected, then the issue's custom field should be updated to the first selected option, and cloned as many times as the number of remaining selected options. After that in each created clone the custom field should be set to only one of the remaining options respectively and clone's issue type updated according to that selection.

For example, the issue has the custom field with options 'a', 'b' and 'c' selected. After the post function is triggered, it should create two clones, each of which will have that custom field set to 'b and 'c' respectively. The original issue will have its custom field value set to 'a'. And all respective issues should have their issue types set according to custom field option selected. On paper it should look something like this:

Before cloning:
original issue (cf = a,b,c; issue type = whatever)
After cloning:
original issue (cf = a; issue type = 'Type A')
first clone (cf = b; issue type = 'Type B')
second clone (cf = c; issue type = 'Type C')
and so on


The code:

This is the code I came up with.

import com.atlassian.jira.component.ComponentAccessor

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def cf = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(13801)
def fieldMgr = ComponentAccessor.getFieldManager()
def optsMgr = ComponentAccessor.getOptionsManager()
def issueSvc = ComponentAccessor.getIssueService()
def issueTypes = fieldMgr.getIssueTypeField().getOptionsForIssue(issue).getAt("id")
def svcList = optsMgr.getOptions(cf.getRelevantConfig(issue))
def svcIssueTypeMap = new LinkedHashMap()
def issueCf = new ArrayList()

for (i = 0; i < issue.getCustomFieldValue(cf).size(); i++){
issueCf.add(issue.getCustomFieldValue(cf)[i])
}

for (int i = 0; i < svcList.size(); i++){
svcIssueTypeMap.put(svcList[i],issueTypes[i])
}
if (issue.getCustomFieldValue(cf).size() > 1){
for (int i = 1; i < issueCf.size(); i++){
def cfValue = new ArrayList()
cfValue.add(issueCf[i])
issue.setCustomFieldValue(cf, cfValue)
issue.setIssueTypeId(svcIssueTypeMap.get(issueCf[i]))
def issueCfMap = new HashMap()
issueCfMap.put(cf,Optional.of(true))
def cloneVld = issueSvc.validateClone(currentUser, issue, issue.getSummary(), true, false, false, issueCfMap)
issueSvc.clone(currentUser, cloneVld)
}
}
def finalCfValue = new ArrayList()
finalCfValue.add(issueCf[0])
issue.setCustomFieldValue(cf, finalCfValue)
issue.setIssueTypeId(svcIssueTypeMap.get(issueCf[0]))
issue.setAssignee(currentUser)

As you can see, my idea is to set the values of the custom field and issue type in the current issue to the values that should be in the clone, then clone the issue, and repeat the process using the values for the next clone and so on. After all clones have been created I finally set the original issue's custom field and issue types the to values they should have.

Everything works just fine as long as the custom field has only one option selected (the easy part). When the custom field has multiple options selected this is what happens:

Before cloning:
original issue (cf = a,b,c; issue type = whatever)
After cloning:
original issue (cf = a; issue type = 'Type A') (good!)
first clone (cf = a,b,c; issue type = 'Type A')
second clone (cf = a,b,c; issue type = 'Type A')

Please advise, what am I doing wrong here?

Cheers,
Ivan

 

1 answer

This widget could not be displayed.

Okay:

So Looking at your code:

if (issue.getCustomFieldValue(cf).size() > 1){
for (int i = 1; i < issueCf.size(); i++){
def cfValue = new ArrayList()
cfValue.add(issueCf[i])
issue.setCustomFieldValue(cf, cfValue)
issue.setIssueTypeId(svcIssueTypeMap.get(issueCf[i]))
def issueCfMap = new HashMap()
issueCfMap.put(cf,Optional.of(true))
def cloneVld = issueSvc.validateClone(currentUser, issue, issue.getSummary(), true, false, false, issueCfMap)
issueSvc.clone(currentUser, cloneVld)
}
}

From what you have said in your code, it would seem like no matter what options you have on your custom field it never gets in this for.

In order to debug this. Before that line, place

log.debug(issue.getCustomFieldValue(cf))

Tell me what that value is when your script doesn't work (Options a b c) afterwards I might be able to help out!

Cheers!

Dyelamos

Ivan Tovbin Community Champion Dec 20, 2017

Hi Dyelamos, 

Thanks for the reply! I actually managed to figure it out on my own. In the end my solution now involves two post-functions:

1) The post-function for the original issue, which creates the clones and also puts the original issue's id into clones' custom field (id 13802).

2) The post-function on 'Create' transition, which takes the id of the original issue for the custom field mentioned above, and uses it to link the newly created clones to the original and also copy all attachments from there.

I've tried incorporating this into a single post-function but with no success, because I suppose that the clones are actually created after the post-function is run, and you can't really link them before that, because they don't exist.

Nevertheless if this is actually possible somehow, then I would greatly appreciate your insight.

Here's the code:

Create clones: 

import com.atlassian.jira.component.ComponentAccessor
import java.sql.Timestamp

def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()

def fieldMgr = ComponentAccessor.getFieldManager()
def optsMgr = ComponentAccessor.getOptionsManager()
def cfMgr = ComponentAccessor.getCustomFieldManager()

def cf = cfMgr.getCustomFieldObject(13801)

//map custom field options on issue types
def issueTypes = fieldMgr.getIssueTypeField().getOptionsForIssue(issue).getAt("id")
def svcList = optsMgr.getOptions(cf.getRelevantConfig(issue))
def svcIssueTypeMap = new LinkedHashMap()

for (int i = 0; i < svcList.size(); i++){
svcIssueTypeMap.put(svcList[i],issueTypes[i])
}

//check for number of custom field options selected
if (issue.getCustomFieldValue(cf).size() > 1){
//def issueFactory = ComponentAccessor.getIssueFactory()
//def issueMgr = ComponentAccessor.getIssueManager()

def svcField = cfMgr.getCustomFieldObject(13802)

//map all custom fields of the original issue
def allIssueCfs = cfMgr.getCustomFieldObjects(issue)
def cfsWithValues = new LinkedHashMap()

for (int i = 0; i < allIssueCfs.size(); i++){
if (allIssueCfs[i].hasValue(issue) == true){
cfsWithValues.put(allIssueCfs[i], issue.getCustomFieldValue(allIssueCfs[i]))
}
}

//create clones
for (int i = 1; i < issue.getCustomFieldValue(cf).size(); i++){
def newIssueTemplate = ComponentAccessor.getIssueFactory().cloneIssue(issue)

//copy custom field values from the original issue
for (int v = 0; v < cfsWithValues.size(); v++){
newIssueTemplate.setCustomFieldValue(cfsWithValues.keySet()[v], cfsWithValues.get(cfsWithValues.keySet()[v]))
}

//record the id of the original issue into clone's special custom field
newIssueTemplate.setCustomFieldValue(svcField, issue.getId() as String)

//set the value of our "checkboxes" custom field and respective issue type
newIssueTemplate.setCustomFieldValue(cf, getCustomFieldValue(cf)[i].grep())
newIssueTemplate.setIssueTypeId(svcIssueTypeMap.get(issue.getCustomFieldValue(cf)[i]))
newIssueTemplate.setCreated(new Timestamp(System.currentTimeMillis()))

ComponentAccessor.getIssueManager().createIssueObject(currentUser, newIssueTemplate)
}
issue.setCustomFieldValue(cf, issue.getCustomFieldValue(cf)[0].grep())
}

issue.setIssueTypeId(svcIssueTypeMap.get(issue.getCustomFieldValue(cf)[0]))
issue.setAssignee(currentUser)

 

Link clones to the original issue and copy all its attachments:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.UpdateIssueRequest

def cf = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(13802)

if (issue.getCustomFieldValue(cf) != null){
//def issueMgr = ComponentAccessor.getIssueManager()
def actor = ComponentAccessor.getUserManager().getUserById(15200).get()
def parentIssue = ComponentAccessor.getIssueManager().getIssueObject(issue.getCustomFieldValue(cf) as Long)
ComponentAccessor.getAttachmentManager().copyAttachments(parentIssue, actor, issue.getKey())
ComponentAccessor.getIssueLinkManager().createIssueLink(issue.getId(), issue.getCustomFieldValue(cf) as Long, 10300, 1, actor)
issue.setCustomFieldValue(cf, null)
ComponentAccessor.getIssueManager().updateIssue(actor, issue, UpdateIssueRequest.builder().build())
}

 

Cheers,

Ivan

Suggest an answer

Log in or Sign up to answer
Atlassian Summit 2018

Meet the community IRL

Atlassian Summit is an excellent opportunity for in-person support, training, and networking.

Learn more
Community showcase
Posted Wednesday in New to Jira

Are you planning to trial, or are currently trialling Jira Software? - We want to talk to you!

Hello! I'm Rayen, a product manager at Atlassian. My team and I are working hard to improve the trial experience for Jira Software Cloud. We are interested in   talking to 20 people planning t...

139 views 2 0
Join discussion

Atlassian User Groups

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

Find a group

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

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you