Groovy

Post-function: Clear issue links and leave a comment in the affected issues

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)

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.

© 2023, STAGIL