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

Ivan Tovbin
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 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

0 votes
Daniel Yelamos [Adaptavist]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 20, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 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