package com.adaptavist
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.bean.PagerFilter
import groovy.transform.Field
import groovy.xml.MarkupBuilder
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.APKeys
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.link.IssueLinkType
import com.onresolve.scriptrunner.parameters.annotation.IssueLinkTypePicker
import com.onresolve.scriptrunner.parameters.annotation.*
import com.onresolve.scriptrunner.parameters.annotation.meta.Option
def issueManager = ComponentAccessor.issueManager
def issueLinkManager = ComponentAccessor.issueLinkManager
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def issueLinkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
@Select(label = "Issue Key or Jql", description = "Select to do a IssueKey or Jql Search", options = [@Option(label = "Issue Key", value = "key"),@Option(label = "JQL", value = "jql")])
String jqlOrKey
@ShortTextInput(label = "Issue key", description = "Enter a issue key")
String issueKey
@ShortTextInput(label = "Jql Search", description = "Enter a valid JQL")
String jqlSearchString
@IssueLinkTypePicker(label = 'Issue link type', description = 'Pick an issue link type', placeholder = 'Pick an issue link type')
IssueLinkType issueLinkType
@Checkbox(label = "Reverse inwards links", description = "Check to reverse inwards links")
Boolean inwardLinksShouldBeReversed
@Checkbox(label = "Reverse outwards links", description = "Check to reverse outwards links")
Boolean outwardLinksShouldBeReversed
@Checkbox(label = "Preview", description = "Check for dry run. If Checked, nothing will actually be changed")
Boolean preview
def results = new ArrayList<>();
Issue issue = null;
if(jqlOrKey != null) {
if (jqlOrKey == "jql" && jqlSearchString != null) {
def issues = jqlSearch(jqlSearchString, loggedInUser, 0)?.getResults()
if(issues != null) {
issues.each { Issue thisIssue ->
results.add(processIssue(issueLinkManager, thisIssue, issueLinkType, issueKey, inwardLinksShouldBeReversed, outwardLinksShouldBeReversed, preview, loggedInUser))
}
return results
}
}
if(jqlOrKey == "key") {
issue = issueManager.getIssueByCurrentKey(issueKey)
assert issue: "no issue found for '${issueKey}'"
return processIssue(issueLinkManager, issue, issueLinkType, issueKey, inwardLinksShouldBeReversed, outwardLinksShouldBeReversed, preview, loggedInUser)
}
}
private String processIssue(IssueLinkManager issueLinkManager, Issue issue, IssueLinkType issueLinkType, String issueKey, boolean inwardLinksShouldBeReversed, boolean outwardLinksShouldBeReversed, boolean preview, loggedInUser) {
log.warn("Processing issue of ${issue}")
def inwardLinks = issueLinkManager.getInwardLinks(issue.id).findAll { it.issueLinkType == issueLinkType }
def outwardLinks = issueLinkManager.getOutwardLinks(issue.id).findAll { it.issueLinkType == issueLinkType }
final Long sequence = 1L
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.style(type: "text/css",
'''
#output, #output td, #output th{
border: 1px solid black;
padding: 1em;
}
#output{
border-collapse: collapse;
}
th {
background: lightgray;
}
''')
builder.p {
p "${inwardLinks.size()} incoming links and ${outwardLinks.size()} outgoing links found for issue ${issueKey} and link type ${issueLinkType.name}."
p "Inward links will ${inwardLinksShouldBeReversed ? '' : 'not'} be reversed"
p "Outward links will ${outwardLinksShouldBeReversed ? '' : 'not'} be reversed"
p { i "${preview ? 'DRY RUN - nothing will be changed' : 'changes will be applied'}" }
table(id: "output") {
tr {
th "Original link"
th "new reversed link"
}
if (inwardLinksShouldBeReversed) {
def newSourceIssue = issue
inwardLinks.each { IssueLink link ->
def newDestinationIssue = link.sourceObject
tr {
td { a href: urlify(link.destinationObject.key), link.destinationObject.key
i link.getIssueLinkType().getInward()
a href: urlify(link.sourceObject.key), link.sourceObject.key
}
td { a href: urlify(newDestinationIssue.key), newDestinationIssue.key
i issueLinkType.getInward()
a href: urlify(newSourceIssue.key), newSourceIssue.key
}
}
if (!preview) {
log.warn "reversing existing link '${link.sourceObject.key} ${link.getIssueLinkType().getOutward()} ${link.destinationObject.key}'"
issueLinkManager.createIssueLink(newSourceIssue.id, newDestinationIssue.id, issueLinkType.id, sequence, loggedInUser)
issueLinkManager.removeIssueLink(link, loggedInUser)
}
}
}
if (outwardLinksShouldBeReversed) {
def newDestinationIssue = issue
outwardLinks.each { IssueLink link ->
def newSourceIssue = link.destinationObject
tr {
td { a href: urlify(link.sourceObject.key), link.sourceObject.key
i link.getIssueLinkType().getOutward()
a href: urlify(link.destinationObject.key), link.destinationObject.key
}
td { a href: urlify(newSourceIssue.key), newSourceIssue.key
i issueLinkType.getOutward()
a href: urlify(newDestinationIssue.key), newDestinationIssue.key
}
}
if (!preview) {
issueLinkManager.createIssueLink(newSourceIssue.id, newDestinationIssue.id, issueLinkType.id, sequence, loggedInUser)
issueLinkManager.removeIssueLink(link, loggedInUser)
log.warn "reversing existing link '${link.sourceObject.key} ${link.getIssueLinkType().getOutward()} ${link.destinationObject.key}'"
}
}
}
}
}
return writer.toString()
}
static def urlify(issuekey) {
def baseUrl = ComponentAccessor.applicationProperties.getString(APKeys.JIRA_BASEURL)
return "${baseUrl}/browse/${issuekey}"
}
SearchResults<Issue> jqlSearch(String jqlString, ApplicationUser user, int index) {
try {
int pageSize = 10000;
SearchService searchService = ComponentAccessor.getComponent(SearchService.class);
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlString);
if (parseResult.isValid()) {
SearchResults<Issue> results = searchService.search(user, parseResult.getQuery(), PagerFilter.newPageAlignedFilter(index, pageSize));
return results;
}
} catch (Exception e) {
log.warn("Error into JQL Search with jqlString ${jqlString}");
}
return null;
}
+1