Use groovy script to transform a grid into subtasks
Introduction
Use the table grid editor to create a list of subtasks
This is a more advanced example how you can create a list of subtasks based on the information collected in the grid.
Then by transitioning the 'Create Subtasks' workflow action, all rows in the action grid with status 'Open' will be transformed into a subtask.
Solution
Setting up the script
Some assumptions
You did setup a standard action grid (like the one which is set by default)
You have configured a subtask issuetype 'action'
You installed the Script Runner
You configured a workflow transition - 'Create subtasks' with a post function 'Script postfunction' pointing to a groovy script on the filesystem
Create subtasks from actions
/**
* Import some packages from jira
*
*/
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.SubTaskManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueFactory
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.config.manager.IssueTypeSchemeManager
import com.atlassian.jira.issue.issuetype.IssueType
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.jira.user.util.UserUtil
import org.apache.log4j.Logger;
import groovy.sql.Sql;
import groovy.sql.GroovyRowResult;
import java.sql.SQLException;
/**
* Setup a logger such that errors and such get logged properly
*/
Logger log = Logger.getLogger("com.idalko.scripts");
/**
* Some parameters for the script
*/
String dbConnectionURL = "jdbc:mysql://localhost:3306/defaultj610?useUnicode=true&characterEncoding=UTF8&sessionVariables=storage_engine=InnoDB";
String dbUsername = "root";
String dbPasswd = "root";
String dbDriver = "com.mysql.jdbc.Driver";
// the gridtable is the table as created in the database
String gridTable = "actions_c11001";
// the ISSUETYPENAME is the name of the subtask that needs to be created
String ISSUETYPENAME = "action";
// Setup a connection to the database
Sql connection;
try {
connection = Sql.newInstance( dbConnectionURL, dbUsername, dbPasswd, dbDriver );
} catch (SQLException e) {
log.error( "Unknown (Issue connecting to the database: ${e.getMessage()}");
return;
}
// Collect all relevant data
List<GroovyRowResult> result = null;
try {
String query = "select * from ${gridTable} where issueId = ${issue.id} and istatus = 'O'";
log.debug("Running query " + query);
result = connection.rows(query);
} catch (SQLException e) {
connection.close();
return "Issue retrieving data from the database: " + e.getMessage();;
}
if (result == null) {
log.debug("No rows returned ...");
connection.close();
return;
}
log.debug("Select returned:\n" + result.join("\n"));
// Setup the environment to create subtasks
JiraAuthenticationContext authenticationContext = ComponentAccessor.getJiraAuthenticationContext()
IssueFactory issueFactory = ComponentAccessor.getIssueFactory();
UserUtil userUtil = ComponentAccessor.getUserUtil();
IssueManager issueManager = ComponentAccessor.getIssueManager();
SubTaskManager subTaskManager = ComponentAccessor.getSubTaskManager();
IssueTypeSchemeManager issueTypeSchemeManager = ComponentAccessor.getIssueTypeSchemeManager();
// Make sure that we have an issueType which exists
IssueType subtaskIssueType = issueTypeSchemeManager.getSubTaskIssueTypesForProject(issue.getProjectObject()).findResult {it.getName() == ISSUETYPENAME ? it : null};
if (subtaskIssueType == null) {
log.error("Issuetype with name ${ISSUETYPENAME} not found");
return;
}
// For all the rows in the resulting set
for (GroovyRowResult row: result) {
String summary = row.get("usummary");
String assigneeKey = row.get("iassignee");
/*
* Prepare information
*/
User reporter = authenticationContext.getUser().getDirectoryUser();
if (reporter == null) {
log.error("Cannot resolve reporter - are you logged in ?");
return;
}
User targetUser = null;
if (userUtil.getUserByKey(assigneeKey) != null) {
targetUser = userUtil.getUserByKey(assigneeKey).getDirectoryUser();
}
/*
* Setup subtask
*/
MutableIssue subtaskIssueObject = issueFactory.getIssue();
subtaskIssueObject.setProjectObject(issue.getProjectObject());
subtaskIssueObject.setIssueTypeObject(subtaskIssueType)
subtaskIssueObject.setSummary(summary);
if (targetUser) {
subtaskIssueObject.setAssignee(targetUser);
}
subtaskIssueObject.setReporter(reporter);
/*
* Commit to the database
*/
Map params = new HashMap();
params.put("issue", subtaskIssueObject);
Issue storeIssue = issueManager.createIssueObject(reporter, params);
subTaskManager.createSubTaskIssueLink(issue, storeIssue, reporter);
}
connection.close();
After the Java API was released in v 1.18.0 you can use it to avoid direct connection to the database.
Create Sub-tasks using Java API
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.IssueService.CreateValidationResult
import com.atlassian.jira.bc.issue.IssueService.IssueResult
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.SubTaskManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueInputParameters
import com.atlassian.jira.issue.comments.CommentManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.fields.config.manager.IssueTypeSchemeManager
import com.atlassian.jira.issue.issuetype.IssueType
import com.atlassian.jira.project.Project
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.plugin.PluginAccessor
import org.apache.log4j.Logger;
// constants
String tgeFieldName = "TGE_TEST";
String targetIssueTypeName = "Sub-task";
String summaryColumnName = "isummary";
String descriptionColumnName = "isummary";
// add a comment
// String commentColumnName = "com";
// copy some columns as custom field values
/*
String currencyColumnName = "cur"; String currencyCustomFieldName = "Currency";
String quotationColumnName = "quant"; String quotationCustomFieldName = "Quotation";
String priceColumnName = "unitprice"; String priceCustomFieldName = "Price";
*/
// set up logger
Logger log = Logger.getLogger("com.idalko.scripts");
// find tge field
PluginAccessor pluginAccessor = ComponentAccessor.getPluginAccessor();
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
CustomField tgeCustomField = customFieldManager.getCustomFieldObjectByName(tgeFieldName);
if (tgeCustomField == null) {
log.error("TGE field with name " + tgeFieldName + " not found");
return;
}
Long tgeCustomFieldId = tgeCustomField.getIdAsLong();
// find current user
JiraAuthenticationContext jiraAuthenticationContext = ComponentAccessor.getOSGiComponentInstanceOfType(JiraAuthenticationContext.class);
Object userObject = jiraAuthenticationContext.getLoggedInUser();
User user = userObject instanceof ApplicationUser ? ((ApplicationUser) userObject).getDirectoryUser() : (User) userObject;
// read the grid data
Class dataManagerClass = pluginAccessor.getClassLoader().findClass("com.idalko.jira.plugins.igrid.api.data.TGEGridTableDataManager");
def tgeGridDataManager = ComponentAccessor.getOSGiComponentInstanceOfType(dataManagerClass);
Long issueId = issue.getId();
List<Map<String, Object>> rows = null;
try {
def callResult = tgeGridDataManager.readGridData(issueId, tgeCustomFieldId, null, null, 0, 10, user);
rows = callResult.getValues();
log.debug(rows.size() + " rows were read from the TGE grid with id " + tgeCustomFieldId);
log.debug("TGE grid content: " + rows);
} catch (Exception e) {
log.error("Grid data cannot be read from the the TGE grid with id " + tgeCustomFieldId + ". Cause: " + e.getMessage());
return;
}
// find target issue type
IssueTypeSchemeManager issueTypeSchemeManager = ComponentAccessor.getIssueTypeSchemeManager();
Project project = issue.getProjectObject();
Collection<IssueType> issueTypes = issueTypeSchemeManager.getSubTaskIssueTypesForProject(project);
IssueType targetIssueType = null;
for (IssueType issueType : issueTypes) {
String issueTypeName = issueType.getName();
if (targetIssueTypeName.equals(issueTypeName)) {
targetIssueType = issueType;
break;
}
}
if (targetIssueType == null) {
log.error("Issuetype with name " + targetIssueTypeName + " not found");
return;
}
// find target custom fields (if it is needed)
/*
CustomField currencyCustomField = customFieldManager.getCustomFieldObjectByName(currencyCustomFieldName);
CustomField quotationCustomField = customFieldManager.getCustomFieldObjectByName(quotationCustomFieldName);
CustomField priceCustomField = customFieldManager.getCustomFieldObjectByName(priceCustomFieldName);
*/
// create sub-tasks
IssueService issueService = ComponentAccessor.getIssueService();
SubTaskManager subTaskManager = ComponentAccessor.getSubTaskManager();
// CommentManager commentManager = ComponentAccessor.getCommentManager();
for (Map<String, Object> row : rows) {
String summary = (String) row.get(summaryColumnName);
String description = (String) row.get(descriptionColumnName);
// String comment = (String) row.get(commentColumnName);
// Object currency = (String) row.get(currencyColumnName);
// Object quotation = (Double) row.get(quotationColumnName);
// Object price = (Double) row.get(priceColumnName);
IssueInputParameters inputParameters = issueService.newIssueInputParameters();
inputParameters.setProjectId(project.getId());
inputParameters.setSummary(summary);
inputParameters.setIssueTypeId(targetIssueType.getId());
inputParameters.setAssigneeId(user.getName());
inputParameters.setReporterId(user.getName());
inputParameters.setDescription(description);
// inputParameters.addCustomFieldValue(currencyCustomField.getIdAsLong(), String.valueOf(currency));
// inputParameters.addCustomFieldValue(quotationCustomField.getIdAsLong(), String.valueOf(quotation));
// inputParameters.addCustomFieldValue(priceCustomField.getIdAsLong(), String.valueOf(price));
inputParameters.setApplyDefaultValuesWhenParameterNotProvided(true);
CreateValidationResult createSubTaskValidationResult = issueService.validateSubTaskCreate(userObject, issueId, inputParameters);
if (!createSubTaskValidationResult.isValid()) {
Collection<String> errors = createSubTaskValidationResult.getErrorCollection().getErrorMessages();
log.error("Sub-task cannot be created due to following reasons: " + errors);
return;
} else {
IssueResult issueResult = issueService.create(userObject, createSubTaskValidationResult);
Issue subTask = issueResult.getIssue();
subTaskManager.createSubTaskIssueLink(issue, subTask, userObject);
log.debug("Sub-task " + subTask + " for issue " + issue + " is created");
// add comment
// commentManager.create(subTask, userObject, null, comment, null, null, new Date(), null, true);
// log.debug("Comment " + comment + " is added to sub-task " + subTask);
}
}