Bob Swift's "Create on Transition" integration
Usecase
You have "Create on Transition" add-on by Bob Swift installed on your Jira. It is configured to create a copy of an issue during the transition. Issue also has a TGE grid with "gd.query" in grid configuration, that depends on custom field values that are not yet set on issue creation moment. Data in original issue is loaded correctly in original issue, but fails in copied issue.
Solution
With TGE API released in 1.18.0 you can perform grid reload from groovy code, i.e. from ScriptRunner post-function. ScriptRunner is the add-on allows to execute arbitrary groovy code on specific JIRA events. To use the suggested solution you have to install ScriptRunner add-on on your JIRA first.
More examples of TGE API usage you can find here:Â Java API: Groovy script examples.
You want to check the "Reload Grid" script. It performs grid reload and takes issue and TGE custom field ID as arguments. TGE custom field IDs can be got using another methods of API. The problem is to find cloned issue to pass it to the reload API call.
Fortunately, Bob Swift cares about his customers and allows to define links that will be applied to newly created issue in configuration of 'Create on transition' post-function. You can configure it to create a link to original issue of type, let's say, 'Cloners'. All the types of links configured can be viewed in Jira Configuration. Also you can configure "Create on Transition" to copy custom field values from original issue to a new one.
Read more about the configuration of the "Create on Transition" add-on:Â https://bobswift.atlassian.net/wiki/display/CSOT/How+to+link+issues+on+transition.
Ok, now the cloned issue is linked with the original with link of type "Cloners". So we can use this knowledge in another scripted post-function, that will be executed after the issue creation, caused by Bob's add-on post-function. It is possible to find all that links using Jira API in that script, find TGE custom field IDs, and perform a grid reload based on copied value of custom fields, TGE query relies on. Here is the script of post-function, that does the job (is't a bit modified "Reload Grid" script from TGE API examples):
Reload Grids
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.bc.issue.link.IssueLinkService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.link.LinkCollection
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.ApplicationUsers
import com.atlassian.plugin.PluginAccessor
// save debug messages
StringBuilder result = new StringBuilder();
// get an issue
IssueManager issueManager = ComponentAccessor.getOSGiComponentInstanceOfType(IssueManager.class);
Issue issue = issueManager.getIssueObject("TEST-1");
// find TGE custom fields
PluginAccessor pluginAccessor = ComponentAccessor.getPluginAccessor();
Class tgeConfigManagerClass = pluginAccessor.getClassLoader().findClass("com.idalko.jira.plugins.igrid.api.config.grid.TGEGridConfigManager");
def tgeConfigManager = ComponentAccessor.getOSGiComponentInstanceOfType(tgeConfigManagerClass);
List<Long> tgeCustomFieldIds = tgeConfigManager.getGridCustomFieldIds();
// get current user
JiraAuthenticationContext jiraAuthenticationContext = ComponentAccessor.getOSGiComponentInstanceOfType(JiraAuthenticationContext.class);
Object userObject = jiraAuthenticationContext.getLoggedInUser();
ApplicationUser applicationUser = userObject instanceof ApplicationUser ? (ApplicationUser) userObject : ApplicationUsers.from(userObject);
User user = userObject instanceof ApplicationUser ? ((ApplicationUser) userObject).getDirectoryUser() : (User) userObject;
// find cloned issues by link reference "Cloners"
IssueLinkService issueLinkService = ComponentAccessor.getOSGiComponentInstanceOfType(IssueLinkService.class);
IssueLinkService.IssueLinkResult issueLinks = issueLinkService.getIssueLinks(userObject, issue);
LinkCollection linkCollection = issueLinks.getLinkCollection();
List<Issue> clonedIssues = linkCollection.getInwardIssues("Cloners");
result.append(clonedIssues.size() + " cloned issues found for issue " + issue.getKey() + "\n");
// reload grids
Class dataManagerClass = pluginAccessor.getClassLoader().findClass("com.idalko.jira.plugins.igrid.api.data.TGEGridTableDataManager");
def tgeGridDataManager = ComponentAccessor.getOSGiComponentInstanceOfType(dataManagerClass);
for (Issue clonedIssue : clonedIssues) {
try {
tgeGridDataManager.reloadGrids(clonedIssue.getId(), new HashSet<Long>(tgeCustomFieldIds), user);
result.append("Grids " + tgeCustomFieldIds + " are reloaded on issue " + clonedIssue.getKey() + "!\n");
} catch (Exception e) {
result.append("Grids cannot be reloaded: " + e.getMessage() + "\n");
}
}
println(result.toString());
return result.toString();
Note: script is compatible with JIRA 7 and later. To make it working on JIRA 6 you have to replace ApplicationUser class with User class.
Here is an another example, where grid rows have to be copied from the original issue to newly created.
Copy rows
import com.atlassian.crowd.embedded.api.User
import com.atlassian.jira.bc.issue.link.IssueLinkService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.link.LinkCollection
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.ApplicationUsers
import com.atlassian.plugin.PluginAccessor
// save debug messages
StringBuilder result = new StringBuilder();
// get an issue
IssueManager issueManager = ComponentAccessor.getOSGiComponentInstanceOfType(IssueManager.class);
Issue issue = issueManager.getIssueObject("TEST-1");
// find TGE custom field
PluginAccessor pluginAccessor = ComponentAccessor.getPluginAccessor();
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
CustomField tgeCustomField = customFieldManager.getCustomFieldObjectByName("TGE_TEST");
Long tgeCustomFieldId = tgeCustomField.getIdAsLong();
// get current user
JiraAuthenticationContext jiraAuthenticationContext = ComponentAccessor.getOSGiComponentInstanceOfType(JiraAuthenticationContext.class);
Object userObject = jiraAuthenticationContext.getLoggedInUser();
ApplicationUser applicationUser = userObject instanceof ApplicationUser ? (ApplicationUser) userObject : ApplicationUsers.from(userObject);
User user = userObject instanceof ApplicationUser ? ((ApplicationUser) userObject).getDirectoryUser() : (User) userObject;
// read grid data from the original issue
Class dataManagerClass = pluginAccessor.getClassLoader().findClass("com.idalko.jira.plugins.igrid.api.data.TGEGridTableDataManager");
def tgeGridDataManager = ComponentAccessor.getOSGiComponentInstanceOfType(dataManagerClass);
List<Map<String, Object>> gridData = null;
try {
def callResult = tgeGridDataManager.readGridData(issue.getId(), tgeCustomFieldId, null, null, 0, 1000, user);
gridData = callResult.getValues();
result.append("Grid ID=" + tgeCustomFieldId + " content: " + gridData + "\n");
} catch (Exception e) {
result.append("Grid ID=" + tgeCustomFieldId + " data cannot be retrieved: " + e.getMessage() + "\n");
}
if (gridData != null) {
// get the list of grid columns
Class tgeConfigManagerClass = pluginAccessor.getClassLoader().findClass("com.idalko.jira.plugins.igrid.api.config.grid.TGEGridConfigManager");
def tgeConfigManager = ComponentAccessor.getOSGiComponentInstanceOfType(tgeConfigManagerClass);
def tgeGrid = tgeConfigManager.getGridConfigOrNull(tgeCustomFieldId);
def columnConfigs = tgeGrid.getColumnConfigs();
List<String> columnConfigNames = new LinkedList<String>();
for (def columnConfig : columnConfigs) {
String columnConfigName = columnConfig.getConfigName();
columnConfigNames.add(columnConfigName);
}
result.append("Grid ID=" + tgeCustomFieldId + " has following columns: " + columnConfigNames + "\n");
// transform fetched data
List<Map<String, Object>> gridDataToAdd = new LinkedList<Map<String, Object>>();
for (Map<String, Object> gridRow : gridData) {
Map<String, Object> rowToAdd = new HashMap<String, Object>();
for (String columnName : columnConfigNames) {
def columnValue = gridRow.get(columnName);
if (columnValue instanceof Map) {
columnValue = columnValue.get("value");
}
rowToAdd.put(columnName, columnValue);
}
gridDataToAdd.add(rowToAdd);
}
result.append("Transformed data to add: " + gridDataToAdd + "\n");
// find cloned issues by link reference "Cloners"
IssueLinkService issueLinkService = ComponentAccessor.getOSGiComponentInstanceOfType(IssueLinkService.class);
IssueLinkService.IssueLinkResult issueLinks = issueLinkService.getIssueLinks(userObject, issue);
LinkCollection linkCollection = issueLinks.getLinkCollection();
List<Issue> clonedIssues = linkCollection.getInwardIssues("Cloners");
result.append(clonedIssues.size() + " cloned issues found for issue " + issue.getKey() + "\n");
// add copied grid data to cloned issue
for (Issue clonedIssue : clonedIssues) {
try {
tgeGridDataManager.addRows(clonedIssue.getId(), tgeCustomFieldId, gridDataToAdd, user);
result.append("Grid rows are added to " + clonedIssue.getKey() + " issue.\n");
} catch (Exception e) {
result.append("Grids rows cannot be added due to error: " + e.getMessage() + "!\n");
}
}
}
println(result.toString());
return result.toString();