Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

Scriptrunner - Set insight custom field value based on issue summary

Tom Brown
Contributor
June 28, 2022

Hi Community,

I have a custom script post-function which creates sub-tasks based on the objects selected in an insight custom field 'Data Sources'; it also sets the Summary of each sub-task as the 'Name' attribute for that insight object. The insight custom field may contain multiple objects and the script iterates through the selected objects creating a sub-task for each.

I also want to set the insight custom field 'Data Sources' in each sub-task with the object that it relates to. I thought that this would be easiest as a post function in the sub-task workflow on the Create transition that would take the Summary and use that to set the 'Data Sources' field (as they will be the same).

However I am struggling and would appreciate any help.

Here is what I have come up with from combining various sources but not quite there. I would really appreciate any help:

import customRiadaLibraries.insightmanager.InsightManagerForScriptrunner
import customRiadaLibraries.insightmanager.SimplifiedAttachmentBean
import com.riadalabs.jira.plugins.insight.services.model.*
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
import java.sql.Connection
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.event.type.EventDispatchOption;
import com.riadalabs.jira.plugins.insight.services.model.ObjectBean
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.IQLFacade
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.onresolve.scriptrunner.runner.customisers.PluginModule

@WithPlugin("com.riadalabs.jira.plugins.insight")
@PluginModule IQLFacade iqlFacade

def summary = issue.getSummary()toString()

/* Insight custom field to set */
CustomField insightCF = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(11700);

/* Get Insight IQL Facade from plugin accessor */
Class iqlFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.IQLFacade");
def iqlFacade = ComponentAccessor.getOSGiComponentInstanceOfType(iqlFacadeClass);

/* Specify the schema id as well as the IQL that will fetch objects. In this case all objects with Name matching the valueCF, be sure to include " around value */
def objects = iqlFacade.findObjectsByIQLAndSchema(16, "Name = \"" + summary + "\"");

issue.setCustomFieldValue(insightCF, objects);

1 answer

1 accepted

1 vote
Answer accepted
PD Sheehan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 28, 2022

How are you creating your subtasks?

Why not populate the data source with the object at that time?

Your existing should work mostly as is... here is a simplified version. I'd recommend including the object type in the IQL to make sure you don't get a false match

import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.IQLFacade

@WithPlugin("com.riadalabs.jira.plugins.insight")
@PluginModule IQLFacade iqlFacade

/* Specify the schema id as well as the IQL that will fetch objects. In this case all objects with Name matching the valueCF, be sure to include " around value */
def objects = iqlFacade.findObjects(16, /objectType = "Your Object Type" and Name = "$issue.summary"/);
if(objects){
/* Insight custom field to set */
def insightCF = ComponentAccessor.customFieldManager.getCustomFieldObject(11700)
issue.setCustomFieldValue(insightCF, objects)
}

A few notes...

1) findObjectsByIQLAndSchema is deprecated, you can just call findObjects with the schema ID as the first parameter

2) I like to use alternate delimiters for IQL strings to allow unescaped double quotes (either /text with (") quotes/ or """text with (") quotes"""

3) I thinks using string interpolation is easier to read than to use "string" + "string"

4) issue.summary is already a string by default, no need to recast

5) @WithPlugin and @PluginModule does the same thing as the classLoader getOSGiComponentInstanceOfType. You shouldn't have both in the same script

6) I re-arranged the order of some code so that you only fetch the custom field IF you actually found an object. 

7) in groovy, you can shorten object.getSomthing() to object.something. Makes for short and easier to read code imo

Tom Brown
Contributor
June 29, 2022

Hi @PD Sheehan ,

Thank you so much for your response. The code you supplied works!

In response to your questions:

  • How are you creating your subtasks?
    • The subtasks are created using a custom script post-function, this is because a sub-task needs to be created for each object selected in the 'Data Sources' insight field of the parent
  • Why not populate the data source with the object at that time?
    • I tried to do this originally but was unsuccessful, I can share the script if you are interested?

I still have an issue though. I wanted this script to be a post function on the create transition but although it does not fail, it does not set the object (I have tried putting as the second, fourth and last post function).

If I put as a post function on the next transition then it works? Any ideas why?

Thanks,

Tom

PD Sheehan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 29, 2022

Shre your create sub-task script, I'll help you get the customfield populated there.

It's better than doing it in the next workflow.

Tom Brown
Contributor
June 29, 2022

@PD Sheehan that would be great thanks. Here is the script; it creates a sub-task for each object selected in an insight/object custom field and also sets the summary of each sub-task as the name of the object.

import customRiadaLibraries.insightmanager.InsightManagerForScriptrunner
import customRiadaLibraries.insightmanager.SimplifiedAttachmentBean
import com.riadalabs.jira.plugins.insight.services.model.*
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
import org.apache.log4j.Logger
import org.apache.log4j.Level
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.config.manager.PrioritySchemeManager
import com.atlassian.jira.issue.label.Label

def mylog = Logger.getLogger("com.acme.workflows")
mylog.setLevel(Level.DEBUG)

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def customFieldInsight = customFieldManager.getCustomFieldObject(12345)
def CusValue = issue.getCustomFieldValue(customFieldInsight)

InsightManagerForScriptrunner im = new InsightManagerForScriptrunner()
im.log.warn("IM is working")

CusValue.eachWithIndex { item, index ->
mylog.info("item="+item)
mylog.info("index="+index)

final parentIssueKey = issue.getKey()
final issueTypeName = 'Sub-task'
final priorityName = 'Medium'

def summary = item.toString() // summary will match the insight object name for the current iteration
def summarySub = summary.substring(0, summary.lastIndexOf(" (")) // removes the insight database ID from the string
def summarySet = summarySub


def issueService = ComponentAccessor.issueService
def constantsManager = ComponentAccessor.constantsManager
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def prioritySchemeManager = ComponentAccessor.getComponent(PrioritySchemeManager)

def parentIssue = ComponentAccessor.issueManager.getIssueByCurrentKey(parentIssueKey)
assert parentIssue : "Could not find parent issue with key $parentIssueKey"

def subtaskIssueTypes = constantsManager.allIssueTypeObjects.findAll { it.subTask }
def subTaskIssueType = subtaskIssueTypes.findByName(issueTypeName)
assert subTaskIssueType : "Could not find subtask issue type with name $issueTypeName. Available subtask issue types are ${subtaskIssueTypes*.name.join(", ")}"

def reporter = issue.getReporter()
def currentUser = loggedInUser

def priorityId = constantsManager.priorities.findByName(priorityName)?.id ?: prioritySchemeManager.getDefaultOption(parentIssue)

def issueInputParameters = issueService.newIssueInputParameters().with {
setProjectId(parentIssue.projectObject.id)
setIssueTypeId(subTaskIssueType.id)
setReporterId(currentUser.username)
setAssigneeId(reporter.username)
setSummary(summarySet)
setPriorityId(priorityId)
}

def validationResult = issueService.validateSubTaskCreate(loggedInUser, parentIssue.id, issueInputParameters)
assert validationResult.valid : validationResult.errorCollection

def issueResult = issueService.create(loggedInUser, validationResult)
assert issueResult.valid : issueResult.errorCollection

def subtask = issueResult.issue
ComponentAccessor.subTaskManager.createSubTaskIssueLink(parentIssue, subtask, loggedInUser)

}
mylog.info("Field Value="+CusValue)
PD Sheehan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 30, 2022

Have a look at the following.

I added some comments. Commented out some sections.

/**
* Used my IDE "optimize" imports which removes unused or unnecessary import
*/
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.config.manager.PrioritySchemeManager
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.riadalabs.jira.plugins.insight.services.model.ObjectBean
import customRiadaLibraries.insightmanager.InsightManagerForScriptrunner
import org.apache.log4j.Level
import org.apache.log4j.Logger

/**
* This annotation allows the import of com.riadalabs classes. Not sure how that worked for you before.
* Maybe something in the insightManager
*/
@WithPlugin('com.riadalabs.jira.plugins.insight') insightPlugin

issue = issue as MutableIssue //this helps with IDE autocomplete and Script Editor status type checking

def mylog = Logger.getLogger("com.acme.workflows")
mylog.setLevel(Level.DEBUG)

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def customFieldInsight = customFieldManager.getCustomFieldObject(12345)
def CusValue = issue.getCustomFieldValue(customFieldInsight) as List<ObjectBean>

/**
* The insightmanager is not really used anywhere in this script
* I recommend removing it
*/
InsightManagerForScriptrunner im = new InsightManagerForScriptrunner()
im.log.warn("IM is working")

/**
* I moved the managers outside the each block. I don't know if once compiled it makes a difference
* But I don't like having these managers and services being executed multiple times.
*/
def issueService = ComponentAccessor.issueService
def constantsManager = ComponentAccessor.constantsManager
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def prioritySchemeManager = ComponentAccessor.getComponent(PrioritySchemeManager)

CusValue.eachWithIndex { item, index ->
mylog.info("item="+item)
mylog.info("index="+index)

final issueTypeName = 'Sub-task'
final priorityName = 'Medium'

/**
* YOU DON't NEED THIS SECTION: get item.label directory
*/
/*
def summary = item.toString() // summary will match the insight object name for the current iteration
def summarySub = summary.substring(0, summary.lastIndexOf(" (")) // removes the insight database ID from the string
def summarySet = summarySub
*/

/**
* YOU DONT't NEED THIS SECTION: the provided "issue" is already the correct object
*/
/*
final parentIssueKey = issue.getKey()
def parentIssue = ComponentAccessor.issueManager.getIssueByCurrentKey(parentIssueKey)
assert parentIssue : "Could not find parent issue with key $parentIssueKey"
*/
def subtaskIssueTypes = constantsManager.allIssueTypeObjects.findAll { it.subTask }
def subTaskIssueType = subtaskIssueTypes.findByName(issueTypeName)
assert subTaskIssueType : "Could not find subtask issue type with name $issueTypeName. Available subtask issue types are ${subtaskIssueTypes*.name.join(", ")}"

/**
* THIS SEEMS A LITTLE UNNECESSARY AND REDUNDANT
*/
/*
def reporter = issue.getReporter() //i changed to issue.reporter.username below
def currentUser = loggedInUser
*/
def priorityId = constantsManager.priorities.findByName(priorityName)?.id ?: prioritySchemeManager.getDefaultOption(issue)

def issueInputParameters = issueService.newIssueInputParameters().with {
setProjectId(issue.projectObject.id)
setIssueTypeId(subTaskIssueType.id)
setReporterId(loggedInUser.username)
setAssigneeId(issue.reporter.username)
setSummary(item.label)
setPriorityId(priorityId)
/**
* Add the object key of the current item to the insight customfield
*/
addCustomFieldValue(customFieldInsight.idAsLong, item.objectKey)
}

def validationResult = issueService.validateSubTaskCreate(loggedInUser, issue.id, issueInputParameters)
assert validationResult.valid : validationResult.errorCollection

def issueResult = issueService.create(loggedInUser, validationResult)
assert issueResult.valid : issueResult.errorCollection

def subtask = issueResult.issue
ComponentAccessor.subTaskManager.createSubTaskIssueLink(issue, subtask, loggedInUser)

}
mylog.info("Field Value="+CusValue)
Tom Brown
Contributor
July 4, 2022

@PD Sheehan wow that really cleans things up for me. Thank you so much!!

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events