Create subtask which are linked to each-other

Adrien SITTER February 3, 2020

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
Fernando Bordallo
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
February 3, 2020

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)

Adrien SITTER February 4, 2020

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...

Adrien SITTER February 4, 2020

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...

Adrien SITTER February 4, 2020

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
Fernando Bordallo
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
February 4, 2020

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