Come for the products,
stay for the community

The Atlassian Community can help you and your team get more value out of Atlassian products and practices.

Atlassian Community about banner
4,365,214
Community Members
 
Community Events
168
Community Groups

Create subtask which are linked to each-other

Edited

Hello everyone,

I try to automatically create 3 subtasks at the creation of a specific issue type.

These subtasks are going to be call A,B and C.

I need A to blocks B and C.

I use ScriptRunner and I created the three subtasks using the "Create a subtask" post function of script-runner without any problem in the creation transition.

Now the linking part get me frustrated.

I tried to put it in several places:

  • In the additional issue actions of both B and C
  • In a postfunction in the workflow of both B and C (creation transition)
  • As a postfunction in the parent function after postfunction "Create a subtask"

It nevers works. When I get the parent issue object and ask for the lists of objects of subtasks (using issue.substasksObjects) it always giving me an empty list.

I think the substasks or at least the link between subtasks and parents are not in the index at this moment. I think so because the same type of code works well in the console when doing it just after creation of subtasks...

Any idea how to do it?

This is the code I use for the last place I tried (post-function in the parent workflow):

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

def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueLinkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
def issueManager = ComponentAccessor.issueManager

def subtasks = issue.subTaskObjects
log.debug(subtasks)

def aIssue = subtasks.find{it.issueType.name == "A"}
assert aIssue: "Subtask A can not be retrieve"

def bIssue = subtasks.find{it.issueType.name == "B"}
assert bIssue: "Subtask B can not be retrieve"

def cIssue = subtasks.find{it.issueType.name == "C"}
assert cIssue: "Subtask C can not be retrieve"

def linkType = issueLinkTypeManager.issueLinkTypes.findByName("Blocks")
assert linkType: "Blocks link type can not be retrieve"

ComponentAccessor.issueLinkManager.createIssueLink(aIssue.id, bIssue.id, linkType.id, 0L, loggedInUser)
ComponentAccessor.issueLinkManager.createIssueLink(aIssue.id, bIssue.id, linkType.id, 1L, loggedInUser)

 First assert give an error.

 

Thank you very much for you help

1 answer

1 accepted

0 votes
Answer accepted

Might be a terrible idea but, have you tried to add a waiting timer after the creation to ensure the parent-subTask links are created later? (given the validation you made that at a later point in time they are there)

Thank you for your answer @Fernando Bordallo

Based on https://community.atlassian.com/t5/Jira-questions/ScriptRunner-and-Post-Function-Scripts-order-and-execution/qaq-p/30485

I tried to put my script in a thread with a while loop and a sleep on it (1000 ms sleep, loop with a watchdog, 100 iteration maximum) but this does not work.

I tried the same on a listener on issue creation with no more luck.

It is just strange because during the time the script run (it tries for more than a minute) I can do the same on the console without any trouble!

Here is my listener:

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

def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueLinkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
def issueManager = ComponentAccessor.issueManager

final int COUNT_MAX = 100;
final int SLEEP_TIME = 1000; //ms

def issue = event.issue
log.warn(issue)

if (["B", "C"].any{it == issue.issueType.name})
{
Thread.start {
log.warn(issue.issueType.name)
def subtasks = issue.parentObject.subTaskObjects
log.warn(subtasks)
def aIssue = subtasks.find{it.issueType.name == "A"}

int count = 0;

while (!aIssue)
{
if (count == COUNT_MAX)
{
log.warn ("Watchdog reached")
return
}
Thread.sleep(SLEEP_TIME);
subtasks = issue.parentObject.subTaskObjects
aIssue = subtasks.find{it.issueType.name == "A"}
log.warn (count);
count++;
}
log.warn(aIssue)

def linkType = issueLinkTypeManager.issueLinkTypes.findByName("Blocks")
log.warn(linkType)

ComponentAccessor.issueLinkManager.createIssueLink(aIssue.id, issue.id, linkType.id, (issue.issueType.name == "B" ? 0L : 1L), loggedInUser)
}

}

 

The first time I tried without a Thread.start. I thinked that every listener execution was running on a different Thread. I was wrong since it run and blocked between each post-function on the transition. The thread works, my post-function are running smoothly but it looks like the Thread can not access the updated index. It is really really strange...

Reading this: https://community.atlassian.com/t5/Answers-Developer-Questions/Post-Function-Order-Trying-to-Set-Assignee-Based-on-Field/qaq-p/512759

I had an idea to verify my script.

I created a self-transition to the status where I put the post-function posted on the original question and add a fast-transition at the end of all post-function at the create transition of issue.

When the transition is automatically made it fails. When I do it manually by clicking after creation, it works...

After few more test I keep asking myself "why the Thread never found the subtasks when it should?" and I finally find it, and as always I said to myself "You're moron".
Like you can see in every script I posted, I get the issue once. When subtasks are note available. Then in a thread I wait and ask the same MutableIssue to give me its subtasks and of course it give me the same answer! I never reload the MutableIssue...
So here is an implementation working, in a postfunction:

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

def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueLinkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
def issueManager = ComponentAccessor.issueManager

// Due to asynchronous indexing that need to be done to retrieve subTasks
// This post-function need to be asynchronous of the other post function
Thread.start {
def reloadedIssue = issue;
def subtasks = reloadedIssue.subTaskObjects
def aIssue = subtasks.find{it.issueType.name == "A"}
def bIssue = subtasks.find{it.issueType.name == "B"}
def cIssue = subtasks.find{it.issueType.name == "C"}
final int MAX_COUNT = 100;
int count = 0;
final int TIME_SLEEP = 1000; //ms

while (!aIssue || !bIssue || !cIssue)
{
if (count == MAX_COUNT)
return
Thread.sleep(TIME_SLEEP);

// We need to reload the MutableObject
reloadedIssue = issueManager.getIssueObject(issue.key)
subtasks = reloadedIssue.subTaskObjects
aIssue = subtasks.find{it.issueType.name == "A"}
bIssue = subtasks.find{it.issueType.name == "B"}
cIssue = subtasks.find{it.issueType.name == "C"}
count++;
}
def linkType = issueLinkTypeManager.issueLinkTypes.findByName("Blocks")

ComponentAccessor.issueLinkManager.createIssueLink(aIssue.id, bIssue.id, linkType.id, 0L, loggedInUser)
ComponentAccessor.issueLinkManager.createIssueLink(aIssue.id, cIssue.id, linkType.id, 1L, loggedInUser)

 

Like Fernando Bordallo likes this

Thanks for updating the thread with your findings! They'll surely come in handy to the next one following your steps :)

Suggest an answer

Log in or Sign up to answer
TAGS

Atlassian Community Events