Script Runner: Subtask creates other subtask on same parent (postfunction)

Hi,

Probably somebody can help me out with this. I'm wondering if there is possible to create (via post-function) on a subtask, to create other one.

In that way, if Subtask A was transitioned, a post-function creates Subtask B.

 

Is there anybody that can help me out?

Thanks!

jay

6 answers

Hello Javier.

Yes this is very possible. 

This has been discussed before, Thanos added an answer to this a while ago. I would suggest you do a thorough search in google before posting, since you can come up with a lot of information that is already out there. 

But since I'm here, I'll tell you how to do this.

You got two options:

The hard way

Basically you need to create a custom scriptfunction that triggers on your transition. 

The customScript should check that the issueType is a subtask, and if it isn't, it shouldn't do anything, but if it is, it should trigger creation of a subtask.

The easy way

However you will find this easier:

Go to postfunctions and add "create subtask" on your transition,

The screen should look like this:

Screen Shot 2017-07-12 at 11.01.23.pngIn the condition field, you should check for the original Issuetype, and if it isn't subtask, you should return false, and if it is, you should return true.

Does this help?

Cheers!

DYelamos

 

Hi Daniel, Thanks for your reply. So, I have tried the easy way and it is working perfectly on the parent issue (postfuncion that creates a sub-task). However, it is not working when the post-function is on the subtask (that is on the same parent), it does nothing.

I updated to the latest version, but it does not work anyway. So, can you please help me out to understand what is going on?

Since I'm getting some presure here at work, I have done this:

1) set a post-function in the sub-task A,

2) that sub-task A ask to the parent issue to execute another post-function in order to create a sub-task B. 

 

Thanks again!

Hello @Daniel Yelamos [Adaptavist],

How can I write the script to do this? (The hard way)

I need a script to put inside of one transition in the subtask workflow, to generate other one subtask in your same parent.
They subtasks will be sisters.

 

 

E.g.:

- Parent A
-- Subtask A1 in parent A
--- The transition nº 100 in the workflow of Subtask A1 have the script to generate a new subtask

When execute the transition nº 100 in the Subtask A1, will be created the Subtask A2 like a sister of the Subtask A1 in parent A

 

Being thus:

- Parent A
-- Subtask A1
-- Subtask A2 (generated by Subtask A1)

Hi Eduardo,

Try a custom script post funtion in the subtask's workflow. You script should be similar to 

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

def issue = issue as MutableIssue

// if the issue in transition is not subtask then do nothing
if (! issue.isSubTask())
return

// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()

def asUser = ComponentAccessor.userManager.getUserByKey("bot")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("A new subtask")
newSubTask.setParentObject(parentIssue)
newSubTask.setPriorityObject(issue.priority) // copy the priority from the subtask in transition
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Sub-task" }.id)

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

Please let me know if this does the trick

kind regards, Thanos 

Hi Thanos,

 

I recieved this error:

 

2017-07-17 13:56:47,679 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2017-07-17 13:56:47,681 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: CMC-1030, actionId: 761, file: <inline script>
com.atlassian.jira.exception.CreateException: An unknown exception occured executing Validator com.atlassian.jira.workflow.SkippableValidator@bc1eaa1: root cause: java.lang.NullPointerException
 at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:588)
 at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:494)
 at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssueObject(DefaultIssueManager.java:599)
 at com.atlassian.jira.issue.managers.RequestCachingIssueManager.createIssueObject(RequestCachingIssueManager.java:198)
 at com.atlassian.jira.issue.IssueManager$createIssueObject$1.call(Unknown Source)
 at Script6854.run(Script6854.groovy:30)
Caused by: com.atlassian.jira.workflow.WorkflowException: An unknown exception occured executing Validator com.atlassian.jira.workflow.SkippableValidator@bc1eaa1: root cause: java.lang.NullPointerException
 at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:760)
 at com.atlassian.jira.issue.managers.DefaultIssueManager.createIssue(DefaultIssueManager.java:580)
 ... 5 more
Caused by: com.opensymphony.workflow.WorkflowException: An unknown exception occured executing Validator com.atlassian.jira.workflow.SkippableValidator@bc1eaa1: root cause: java.lang.NullPointerException
 at com.opensymphony.workflow.AbstractWorkflow.verifyInputs(AbstractWorkflow.java:1523)
 at com.opensymphony.workflow.AbstractWorkflow.transitionWorkflow(AbstractWorkflow.java:1203)
 at com.opensymphony.workflow.AbstractWorkflow.initialize(AbstractWorkflow.java:615)
 at com.atlassian.jira.workflow.OSWorkflowManager.createIssue(OSWorkflowManager.java:741)
 ... 6 more
Caused by: java.lang.NullPointerException
 at com.atlassian.jira.workflow.validator.AbstractPermissionValidator.hasUserPermission(AbstractPermissionValidator.java:38)
 at com.atlassian.jira.workflow.validator.PermissionValidator.validate(PermissionValidator.java:37)
 at com.atlassian.jira.workflow.SkippableValidator.validate(SkippableValidator.java:45)
 at com.opensymphony.workflow.AbstractWorkflow.verifyInputs(AbstractWorkflow.java:1512)
 ... 9 more

 

 

 

Thanks. 

Hi Eduardo, 

Did you change the 

def asUser = ComponentAccessor.userManager.getUserByKey('bot')

to point to a userkey that has permissions to create issues ? 

Sorry Thanos, I forgot this detail. 

 

Can you tell me how I can add in this script to link the two subtasks?

Add the following lines in the end of your script 

def sourceIssueId = issue.id
def destinationIssueId = newSubTask.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "Cloners"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name Cloners"
}

 Now this will create a clone issue between the subtask in transition and the new subtask. Change the name of the issue type you want to set. 

hope that helps, 

Thanos

Thanos,

 

If I use this lastesd lines, I receive the error:

2017-07-17 15:02:21,243 ERROR [workflow.ScriptWorkflowFunction]: *************************************************************************************
2017-07-17 15:02:21,243 ERROR [workflow.ScriptWorkflowFunction]: Script function failed on issue: CMC-1030, actionId: 761, file: <inline script>
java.lang.NullPointerException
 at com.atlassian.jira.issue.link.DefaultIssueLinkManager.getIssueLink(DefaultIssueLinkManager.java:373)
 at com.atlassian.jira.issue.link.DefaultIssueLinkManager.createIssueLink(DefaultIssueLinkManager.java:83)
 at com.atlassian.jira.issue.link.IssueLinkManager$createIssueLink$1.call(Unknown Source)
 at Script6897.run(Script6897.groovy:33)


But If I use the "Inward Description" of the link (e.g. "is cloned by"), I'm not receive the error, create other subtask, but don't links the subtasks.

 

The script now:

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.issue.MutableIssue

def issue = issue as MutableIssue

// if the issue in transition is not subtask then do nothing
if (! issue.isSubTask())
return

// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()

def asUser = ComponentAccessor.userManager.getUserByKey("user")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("A new subtask test")
newSubTask.setParentObject(parentIssue)
newSubTask.setPriorityObject(issue.priority) // copy the priority from the subtask in transition
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Licensing (new)" }.id)

def sourceIssueId = issue.id
def destinationIssueId = newSubTask.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "is cloned by"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name is cloned by"
}

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

Hey eduardo, 

As I mentioned you have to add those lines (that create the issue link) in the end of your script.

Because in order to create the link you will need to first have the source and destination issues created.

regards, Thanos

 

Sorry for my mistake! Again...

 

But don't work yet.

 

The script now:

 

import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issue = issue as MutableIssue

// if the issue in transition is not subtask then do nothing
if (! issue.isSubTask())
return

// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()

def asUser = ComponentAccessor.userManager.getUserByKey("user")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("A new subtask test")
newSubTask.setParentObject(parentIssue)
newSubTask.setPriorityObject(issue.priority) // copy the priority from the subtask in transition
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Licensing (new)" }.id)

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

def sourceIssueId = issue.id
def destinationIssueId = newSubTask.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "Depends of Licensing"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name Depends of Licensing"
}  

 

Do you get any errors in your logs ?

Thanos,

 

The Log error:

image.png

 

 

See the image attached of my link Issues configuration.image.png

Eduardo, 

Try to use Licensing Dependency in the name of the issue link type instead of Depends on Licensing.

 

Error...

java.lang.NullPointerException
at com.atlassian.jira.issue.link.DefaultIssueLinkManager.getIssueLink(DefaultIssueLinkManager.java:373)
at com.atlassian.jira.issue.link.DefaultIssueLinkManager.createIssueLink(DefaultIssueLinkManager.java:83)
at com.atlassian.jira.issue.link.IssueLinkManager$createIssueLink$6.call(Unknown Source)
at Script6945.run(Script6945.groovy:40)

Hi Thanos,

 

I think the problem was because of the outdated version of Script Runner.

 

Now works with the script in the lastest version (5.0.11):

 

import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issue = issue as MutableIssue

// if the issue in transition is not subtask then do nothing
if (! issue.isSubTask())
return

// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()

def asUser = ComponentAccessor.userManager.getUserByKey("user")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("CMC Licensing - " + issue.parentObject.summary)
newSubTask.setParentObject(parentIssue)
newSubTask.setDescription(issue.parentObject.description)
newSubTask.setPriorityObject(issue.priority) // copy the priority from the subtask in transition
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Licensing (new)" }.id)

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

def sourceIssueId = issue.id
def destinationIssueId = newSubTask.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "Licensing Dependency"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name Depends of Licensing Dependency"
}

 

But Is linking with the inverse sort of "Inward / Outward" link that I need.
I need set to link "Inward Description" (Depends of Licensing) 

 

e.g.:

With this script, where is setted "Licensing Dependency" name of link, when the subtask A generate the subtask B, the subtask A received the link "Licensing to" and subtask B "Depends of licensing". I need the reverse of this, as subtask A receive "Depends of Licensing" and subtask B receive "Licensing to".

 

Can you tel me how I do this?

Thanks.

Hi Eduardo,

Cool. So in order to reverse the link direction just cahneg the lines below like 

def sourceIssueId = newSubTask.id

def destinationIssueId = issue.id

May I ask in which ScriptRunner version you were before the update ?

kind regards 

Thanos

GREAT!!!

WORKS NOW!!! 

 

I revise and checked that it works also in the old version. (my older version is 5.0.1)

 

Thank you so much Thanos.

@Thanos Batagiannis [Adaptavist]

One more little help, please...

 

How I add a condition to verify one custom field before follow the script?

If the custom field (select list) "customfield_15223" == 'Yes', generate the subtask...

 

Thanks.

Latest script that working, for anyone that need...

 

 

import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issue = issue as MutableIssue

// if the issue in transition is not subtask then do nothing
if (! issue.isSubTask())
return

// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()

def asUser = ComponentAccessor.userManager.getUserByKey("user")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("[CMC] Licensing - " + issue.parentObject.summary) // copy the summary from the parent adding a text
newSubTask.setParentObject(parentIssue)
newSubTask.setDescription(issue.parentObject.description)// copy the description from the parent
newSubTask.setPriorityObject(issue.parentObject.priority) // copy the priority from the parent
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Licensing (new)" }.id)

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

def sourceIssueId = newSubTask.id
def destinationIssueId = issue.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "Licensing Dependency"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name Depends of Licensing Dependency"
}

 

 

Latest script to verifying first a custom field value for create the sister subtask depending on the this custom field value:

 

import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue

def issue = issue as MutableIssue

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cf = customFieldManager.getCustomFieldObject("customfield_15223");
def cfvalue = issue.getCustomFieldValue(cf)


if (! issue.isSubTask() || issue.getCustomFieldValue(cf).toString().contains('Yes')){


// get the parent issue
def parentIssue = issue.parentObject

def issueFactory = ComponentAccessor.getIssueFactory()
def subTaskManager = ComponentAccessor.getSubTaskManager()
def constantManager = ComponentAccessor.getConstantsManager()
def issueManager = ComponentAccessor.getIssueManager()


def asUser = ComponentAccessor.userManager.getUserByKey("user")
def newSubTask = issueFactory.getIssue()

newSubTask.setSummary("[CMC] Licensing - " + issue.parentObject.summary) // copy the summary from the parent adding a text
newSubTask.setParentObject(parentIssue)
newSubTask.setDescription(issue.parentObject.description)// copy the description from the parent
newSubTask.setPriorityObject(issue.parentObject.priority) // copy the priority from the parent
newSubTask.setProjectObject(parentIssue.getProjectObject())
newSubTask.setIssueTypeId(constantManager.getAllIssueTypeObjects().find { it.getName() == "Licensing (new)" }.id)

// Add any other fields you want for the newly created sub task ...

Map<String,Object> newIssueParams = ["issue" : newSubTask] as Map<String,Object>
issueManager.createIssueObject(asUser, newIssueParams)
subTaskManager.createSubTaskIssueLink(parentIssue, newSubTask, asUser)
log.info "Issue with summary ${newSubTask.summary} created"

def sourceIssueId = newSubTask.id
def destinationIssueId = issue.id

def linkTypeId = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes.find {it.name == "Licensing Dependency"}?.id
if (linkTypeId) {
ComponentAccessor.issueLinkManager.createIssueLink(sourceIssueId, destinationIssueId, linkTypeId, 0L, asUser)
}
else {
log.warn "Could not find link type with name Depends of Licensing Dependency"
}
}

 

Suggest an answer

Log in or Sign up to answer
How to earn badges on the Atlassian Community

How to earn badges on the Atlassian Community

Badges are a great way to show off community activity, whether you’re a newbie or a Champion.

Learn more
Community showcase
Published Apr 30, 2018 in Marketplace Apps

Why moving to Confluence Data Center is only a job half-done

Confluence Data Center is a deployment option that was designed for performance at scale. In other words, Confluence will handle increased traffic much reliably in a clustered environment and yo...

251 views 0 6
Read article

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