Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Post-function:

...

Clear issue links and leave a comment in the affected issues

Code Block
breakoutModewide
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.link.IssueLinkTypeManager
 
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def commentManager = ComponentAccessor.getCommentManager()
def adminUser = ComponentAccessor.getUserManager().getUserByName("jira-automat") //to be replaced with actual admin user by providing user name

def issueLinkIdToBeChanged = 10500 as long //"split to" link, supposed to be changed to default Epic Link or SAJ link ("Child Epics", "Parent Epic") depending on issue types
def defaultEpicLinkTypeId = 10001 as long; //default Epic Link id; to be replaced with actual Id
def sajEpicToEpicLinkTypeId = 12345 as long; //custom link created with SAJ ("Child Epics", "Parent Epic"); to be replaced with actual Id

def availableIssueLinkTypes = ComponentAccessor.getComponent(IssueLinkTypeManager).issueLinkTypes
def sajEpicToEpicLinkType = availableIssueLinkTypes.findByName("Epic-to-Epic") //custom link created with SAJ ("Child Epics", "Parent Epic"); to be replaced with actual link type name
def epicFieldName = "Epic Link" //if German language in use: "Epic-Verknüpfung"
 
/* ---------- Get and check all outward links of current issue ---------- */
def outwardLinks = issueLinkManager.getOutwardLinks(issue.id) //current issue --- outward Link > destination issue
for (link in outwardLinks) {
    def destinationIssue = issueManager.getIssueObject(link.destinationId)
    if (link.issueLinkType.getId() == defaultEpicLinkTypeId) { //if an OUTWARD Epic Link exists... (current issue --- outward Epic Link > destination issue)
        /* ---------- Change or remove invalid Epic Links and leave comment ---------- */
        if (destinationIssue.getIssueType().name == "Epic") {
            if (issue.getIssueType().name == "Epic") {
            //IF: Epic --- outward Epic Link > Epic
                issueLinkManager.changeIssueLinkType(link, sajEpicToEpicLinkType, adminUser)
                //THEN: Epic --- "Child Epics" link > Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Invalid outward default Epic Link to Epic " + destinationIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Invalid inward default Epic Link from Epic " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            } else {
            //IF: current issue (any type) --- outward Epic Link > Epic
                issueLinkManager.removeIssueLink(link, adminUser)
                //THEN: remove link
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Invalid outward default Epic Link to Epic " + destinationIssue.getKey() + " removed.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Invalid inward default Epic Link from " + issue.getIssueType().name + " " + issue.getKey() + " removed.{color}", false)
            }       
        }
    } else if (link.issueLinkType.getId() == sajEpicToEpicLinkTypeId) { //if a "Child Epics" link exists... (current issue --- "Child Epics" link > destination issue)
        /* ---------- Remove incorrect SAJ links ("Child Epics", "Parent Epic") and leave comment ---------- */
        if (issue.getIssueType().name == "Milestone" || issue.getIssueType().name == "Task" || issue.getIssueType().name == "Story") {
        //IF: Milestone/Task/Story --- "Child Epics" link > destination issue (any type)
            issueLinkManager.removeIssueLink(link, adminUser)
            //THEN: remove link
            commentManager.create(issue, adminUser, "(!) {color:#505F79}Incorrect \"Child Epics\" link to " + destinationIssue.getIssueType().name + " " + destinationIssue.getKey() + " removed.{color}", false)
            commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Incorrect \"Parent Epic\" link to " + issue.getIssueType().name + " " + issue.getKey() + " removed.{color}", false)
        }
    } else if (link.issueLinkType.getId() == issueLinkIdToBeChanged) { //if a "split to" link exists... (current issue --- "split to" link > destination issue)
        /* ---------- Change "split to" link to default Epic Link or SAJ link ("Child Epics", "Parent Epic") depending on issue types ---------- */
        if (issue.getIssueType().name == "Epic") {
            if (destinationIssue.getIssueType().name == "Epic" || destinationIssue.getIssueType().name == "Milestone") {
            //IF: Epic --- "split to" link > Epic/Milestone
                issueLinkManager.changeIssueLinkType(link, sajEpicToEpicLinkType, adminUser)
                //THEN: Epic --- "Child Epics" link > Epic/Milestone
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + destinationIssue.getIssueType().name + " " + destinationIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            } else if (destinationIssue.getIssueType().name == "Task" || destinationIssue.getIssueType().name == "Story") {
            //IF: Epic --- "split to" link > Task/Story
                issueLinkManager.removeIssueLink(link, adminUser)
                def epicLinkCustomField = customFieldManager.getCustomFieldObjects(destinationIssue).findByName(epicFieldName)
                destinationIssue.setCustomFieldValue(epicLinkCustomField, issue)
                //THEN: Epic --- outward Epic Link > Task/Story
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + destinationIssue.getIssueType().name + " " + destinationIssue.getKey() + " replaced with outward default Epic Link.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + issue.getKey() + " replaced with inward default Epic Link.{color}", false)
            }
        } else if (issue.getIssueType().name == "Milestone") {
            if (destinationIssue.getIssueType().name == "Epic") {
            //IF: Milestone --- "split to" link > Epic
                issueLinkManager.removeIssueLink(link, adminUser)
                issueLinkManager.createIssueLink(destinationIssue.id, issue.id, sajEpicToEpicLinkTypeId, 1L, adminUser)
                //THEN: Milestone --- "Parent Epic" link > Epic / Milestone < "Child Epics" link --- Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + destinationIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to Milestone " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            }
        } else if (issue.getIssueType().name == "Task" || issue.getIssueType().name == "Story") {
            if (destinationIssue.getIssueType().name == "Epic") {
            //IF: Task/Story --- "split to" link > Epic
                issueLinkManager.removeIssueLink(link, adminUser)
                def epicLinkCustomField = customFieldManager.getCustomFieldObjects(issue).findByName(epicFieldName)
                issue.setCustomFieldValue(epicLinkCustomField, destinationIssue)
                //THEN: Task/Story < inward Epic Link --- Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + destinationIssue.getKey() + " replaced with inward default Epic Link.{color}", false)
                commentManager.create(destinationIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + issue.getIssueType().name + " " + issue.getKey() + " replaced with outward default Epic Link.{color}", false)
            }
        }
    }
    issueManager.updateIssue(adminUser, destinationIssue, EventDispatchOption.ISSUE_UPDATED, false)
}

/* ---------- Get and check all inward links of current issue ---------- */
def inwardLinks = issueLinkManager.getInwardLinks(issue.id) //current issue <--- inward Link --- source issue
for (link in inwardLinks) {
    def sourceIssue = issueManager.getIssueObject(link.sourceId)
    if (link.issueLinkType.getId() == defaultEpicLinkTypeId) { //if an INWARD Epic Link exists... (current issue < inward Epic Link --- source issue)
        /* ---------- Change or remove invalid Epic Links and leave comment ---------- */
        if (issue.getIssueType().name == "Epic") {
            if (sourceIssue.getIssueType().name == "Epic") {
            //IF: Epic < inward Epic Link --- Epic
                issueLinkManager.changeIssueLinkType(link, sajEpicToEpicLinkType, adminUser)
                //THEN: Epic --- "Parent Epic" link > Epic / Epic < "Child Epics" link --- Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Invalid inward default Epic Link from Epic " + sourceIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Invalid outward default Epic Link to Epic " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            } else {
            //IF: Epic < inward Epic Link --- Task/Story
                issueLinkManager.removeIssueLink(link, adminUser)
                //THEN: remove link
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Invalid inward default Epic Link from " + sourceIssue.getIssueType().name + " " + sourceIssue.getKey() + " removed.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Invalid outward default Epic Link to Epic " + issue.getKey() + " removed.{color}", false)
            }       
        }
    } else if (link.issueLinkType.getId() == sajEpicToEpicLinkTypeId) { //if a "Parent Epic" link exists... (current issue < "Child Epics" Link --- source issue (inward) / current issue --- "Parent Epic" Link > source issue (outward))
        /* ---------- Remove incorrect SAJ links ("Child Epics", "Parent Epic") and leave comment ---------- */
        if ((issue.getIssueType().name == "Milestone" && sourceIssue.getIssueType().name != "Epic") || issue.getIssueType().name == "Task" || issue.getIssueType().name == "Story") {
        //IF: Milestone < "Child Epics" Link --- source issue (any type except Epic); OR: Task/Story < "Child Epics" Link --- source issue (any type)
            issueLinkManager.removeIssueLink(link, adminUser)
            //THEN: remove link
            commentManager.create(issue, adminUser, "(!) {color:#505F79}Incorrect \"Parent Epic\" link to " + sourceIssue.getIssueType().name + " " + sourceIssue.getKey() + " removed.{color}", false)
            commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Incorrect \"Child Epics\" link to " + issue.getIssueType().name + " " + issue.getKey() + " removed.{color}", false)
        }
    } else if (link.issueLinkType.getId() == issueLinkIdToBeChanged) { //if a "split to" link exists... (current issue < "split to" link --- source issue)
        /* ---------- Change "split to" link to default Epic Link or SAJ link ("Child Epics", "Parent Epic") depending on issue types ---------- */
        if (sourceIssue.getIssueType().name == "Epic") {
            if (issue.getIssueType().name == "Epic" || issue.getIssueType().name == "Milestone") {
            //IF: Epic/Milestone < "split to" link --- Epic
                issueLinkManager.changeIssueLinkType(link, sajEpicToEpicLinkType, adminUser)
                //THEN: Epic/Milestone --- "Parent Epic" link > Epic / Epic/Milestone < "Child Epics" Link --- Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + sourceIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + issue.getIssueType().name + " " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            } else if (issue.getIssueType().name == "Task" || issue.getIssueType().name == "Story") {
            //IF: Task/Story < "split to" link --- Epic
                issueLinkManager.removeIssueLink(link, adminUser)
                def epicLinkCustomField = customFieldManager.getCustomFieldObjects(issue).findByName(epicFieldName)
                issue.setCustomFieldValue(epicLinkCustomField, sourceIssue)
                //THEN: Task/Story < inward Epic Link --- Epic
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + sourceIssue.getKey() + " replaced with inward default Epic Link.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + issue.getIssueType().name + " " + issue.getKey() + " replaced with outward default Epic Link.{color}", false)
            }
        } else if (sourceIssue.getIssueType().name == "Milestone") {
            if (issue.getIssueType().name == "Epic") {
            //IF: Epic < "split to" link --- Milestone
                issueLinkManager.removeIssueLink(link, adminUser)
                issueLinkManager.createIssueLink(issue.id, sourceIssue.id, sajEpicToEpicLinkTypeId, 1L, adminUser)
                //THEN: Epic --- "Child Epics" link > Milestone / Epic < "Parent Epic" link --- Milestone
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to Milestone " + sourceIssue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + issue.getKey() + " replaced with \"" + sajEpicToEpicLinkType.getName() + "\" link.{color}", false)
            }
        } else if (sourceIssue.getIssueType().name == "Task" || sourceIssue.getIssueType().name == "Story") {
            if (issue.getIssueType().name == "Epic") {
            //IF: Epic < "split to" link --- Task/Story
                issueLinkManager.removeIssueLink(link, adminUser)
                def epicLinkCustomField = customFieldManager.getCustomFieldObjects(sourceIssue).findByName(epicFieldName)
                sourceIssue.setCustomFieldValue(epicLinkCustomField, issue)
                //THEN: Epic --- outward Epic Link > Task/Story
                commentManager.create(issue, adminUser, "(!) {color:#505F79}Link \"split to\" to " + sourceIssue.getIssueType().name + " " + sourceIssue.getKey() + " replaced with outward default Epic Link.{color}", false)
                commentManager.create(sourceIssue, adminUser, "(!) {color:#505F79}Link \"split to\" to Epic " + issue.getKey() + " replaced with inward default Epic Link.{color}", false)
            }
        }
    }
    issueManager.updateIssue(adminUser, sourceIssue, EventDispatchOption.ISSUE_UPDATED, false)
}
 
issueManager.updateIssue(adminUser, issue, EventDispatchOption.ISSUE_UPDATED, false)
Info

PLEASE NOTE

  • Line 9: User name to be provided for performing this script.

  • Line 11: Issue link id to be checked and replaced with actual id, if needed. It represents the issue link, which is supposed to be changed to default Epic Link or SAJ link ("Child Epics", "Parent Epic") depending on issue types (for example while performing a CSV import).

  • Line 12: Issue link id to be checked and replaced with actual id, if needed. It represents the SAJ link ("Child Epics", "Parent Epic").

  • Line 13: Issue link id to be checked and replaced with actual id, if needed. It represents the default Epic Link.

  • Line 16: Issue link name to be checked and replaced with actual name, if needed. It represents the SAJ link ("Child Epics", "Parent Epic").

  • Line 17: Epic field name to be checked and replaced with actual name, if needed. Only needed, if indexing language is not English.

⚠Script is restricted to the issue types Epic, Milestone, Task and Story.