High speed issue linking in Jira Service Desk

High-speed issue linking in Jira Service Desk

This is the third in a series of blogs aimed at making your incident resolution quicker and your customers happier. This time we look at how you can link your Incidents, Alerts and Remedial Actions to current and recent Problems, using a self-referencing transition in your workflow.

Linking related issues together is a powerful feature of Jira which allows you to better diagnose issues and manage your work. If you’re a frequent Jira user, you would have likely linked issues before and if you have, you may have found that linking issues is a manual and labour-intensive process. A process that requires you to know exactly what issues should be linked. What if we could speed up this process and reduce the potential for issues to be missed?

In order to create this functionality we need to reuse some of the configuration that we built in the previous two articles. Let’s quickly recap on what we covered in these two articles and what we’ll reuse.

In the first blog we created new issuetypes and established appropriate links between them. We’ll be using both the issuetypes and links in this tip. Note that if you’ve given different names to your issuetypes or links be sure to update the code that follows to match.

In the second blog we saw how we could use JQL to find unresolved and recently-resolved problems. We will be reusing the following JQL to restrict our selection to current and recent Problem Issuetypes.

(issuetype=Problem and (resolution is EMPTY OR resolutiondate > -7d)

Linking the problem ticket

The first step in linking tickets is to create a custom field. This custom field needs to be set as a Hideable Text Field and labelled as “Known Problem”. To build this Hideable Text Field select the <255 characters option in the Advanced Select Field Type. We’re selecting this option as we only need to store the Problem issue key in the field.

Select a field type

Next, we want to make sure that this field is only available for the following Issuetypes Incident, Alert, Remedial Action. This means that we need to set the configuration to only these three Issuetypes. At this stage we could also restrict this functionality to certain projects but for this example, we’re only restricting our use of the Issuetypes with an Issuetype scheme at the project level.

Configure custom field Known Problem

Now that we have our custom field we create a screen called “Link to recent Problem” that we can use on the transition. For this screen, we are going to keep the information requested to the Known Problem field and of course the standard comment field.

Updating your workflow

Link to Known Problem

After we’ve added our screen, the next step is to edit the workflow(s) that are associated with the issuetypes Incident, Alert, and Remedial Action. We’re going to use a little-known, but powerful, piece of functionality, the ability for a workflow to transition from any status back to the same status. While adding a circular workflow sounds rather odd, critically it allows us to add a transition and where you have a transition you can add a transition screen. This appears on the workflow diagram as shown (above, right).

So let us look at the steps to create this transition. First we’ll add a transition and fill in the options as shown below.

Add Transition

Note: The two status fields use special values that don’t refer to the single status.

Next we need to add the following condition to the workflow to make sure this is only available for the appropriate issuetypes.

ScriptRunner workflow functions

We could also add a property to the transition to place the transition as the first option.

Transition properties

The final step in the workflow stage is to publish it. Once you’ve done this, remember to check that the right screen loads on the transition.

Adding behaviours

Our next step is to add the behaviour to your new field so that it only offers the choices from current or recent Problems. In order to add the behaviour navigate to:

<yourhost>/secure/admin/Behaviours!default.jspa

Select create new behaviour and give your new behaviour the following name and description:

NameDescription
Link to an existing ProblemThe behaviour to be taken when the Known Problem field is displayed and has no value

Then map the following fields:

Behaviour IDLink to an existing Problem
Issue TypesIncident, Alert, Remedial Action
Choose Project<Select as appropriate>

Next add an initialiser function with the following code. This initialiser function will be called whenever the field is presented on a screen for editing.


getFieldByName("Known Problem").convertToSingleSelect([
 
ajaxOptions: [
  url : getBaseUrl() + "/rest/scriptrunner-Jira/latest/issue/picker",
  query: true, // keep going back to the sever for each keystroke
 
  // this information is passed to the server with each keystroke
  data: [
    currentJql: "issuetype=Problem AND (resolution is EMPTY OR resolutiondate > -7d) ORDER BY resolution, key ASC",
    label     : "Pick from Open or Recent Problems",
    showSubTasks: false,
    // specify maximum number of issues to display, defaults to 10
    max: 5,
    ],
     formatResponse: "issue"
  ],
  //Specify css for the size of the dropdown maximum width
  css: "max-width: 500px; width: 500px",
]
)

Lastly, place a listener on the custom field in order to make sure that whenever a value is entered the links on the issue are updated to include the related Problem.

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.link.Direction
import com.atlassian.Jira.issue.link.IssueLinkTypeManager
import com.atlassian.Jira.user.ApplicationUser
  
// Constants
final PROBLEM_FIELD = "customfield_10501"
final INCIDENT_LINK = "Incident Link"
final ALERT_LINK = "Alert Link"
final REMEDIAL_LINK = "Remedial Action Link"
  
def createIssueLink(Issue startIssue,
            Issue issueToLink,
            Long linkTypeId,
            Direction linkDirection,
            ApplicationUser updateUser) {
  
  // Get the issue service
  final issueLinkService = ComponentAccessor.getComponent(IssueLinkService)
  
  // Validate the link creation
  final addIssueLinkValidationResult =
       issueLinkService.validateAddIssueLinks(updateUser,
                  startIssue,
                  linkTypeId,
                  linkDirection,
                  [issueToLink.key],
                  true)
  
  // Create the actual link
  if (addIssueLinkValidationResult.valid) {
     issueLinkService.addIssueLinks(updateUser, addIssueLinkValidationResult)
  }
}
  
List getLinkedIssues(Issue rootIssue,
          String linkType) {
  
  // Managers
  final issueLinkManager = ComponentAccessor.issueLinkManager
  
  // The list to hold our found issues
  def outIssues = []
  
  // Get the outward links of the issue and find the matching ones
  final outwardLinks = issueLinkManager.getOutwardLinks(rootIssue.id)
  final foundOutLinks = outwardLinks.findAll {
     it.issueLinkType.name in linkType
  }
  
  // Find the linked issues and return
  foundOutLinks.each{outIssues.add(it.destinationObject)}
  return outIssues
}
  
// Managers
final customFieldManager = ComponentAccessor.customFieldManager
final issueManager = ComponentAccessor.issueManager
final linkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
final authContext = ComponentAccessor.JiraAuthenticationContext
  
// Get the issue from the event and get the value fo the field
final issue = event.issue
final customField = customFieldManager.getCustomFieldObject(PROBLEM_FIELD)
final issueFieldValue  = customField.getValue(issue) as String
  
//Create a variable to hold the appropriate link depending on whether the current issue is an incident or an aler
  
def PROBLEM_LINK = ""
if(issue.getIssueType().getName() == "Incident"){
    PROBLEM_LINK = INCIDENT_LINK
}else if(issue.getIssueType().getName() == "Alert"){
    PROBLEM_LINK = ALERT_LINK
}else if(issue.getIssueType().getName() == "Remedial Action"){
    PROBLEM_LINK = REMEDIAL_LINK
}
  
//If we have a Problem_Link, check for new or existing links
if (PROBLEM_LINK != "") {
// If the customfield has a value, do something
  if ( issueFieldValue) {
  
  // Get all the linked issues and exit if the issue is already one
  final linkedIssues = getLinkedIssues(issue, PROBLEM_LINK).collect{it.key}
  if (issueFieldValue in linkedIssues) {return}
  
  // Get the bits we need for the link
  final currentUser = authContext.loggedInUser
  final issueLink = linkTypeManager.getIssueLinkTypesByName(PROBLEM_LINK).first()
  final linkIssue = issueManager.getIssueByCurrentKey(issueFieldValue)
  
  // Make the link
  createIssueLink(issue, linkIssue, issueLink.id, Direction.OUT, currentUser)
  
  }
}

You should now test that your system is working and that when a new Known Problem is associated with an Incident, Alert or Remedial Action the issue links are updated correctly.

Our next tip looks at how we can quickly create a new problem ticket from inside an Incident or Alert. To find out more about how ScriptRunner for Jira can help you extend, automate, and customise your Jira instance, start a free 30 day trial on the Atlassian Marketplace.