При импорте проектов с использованием project configurator могут создаваться дубли типов линков. Это вызывает проблемы с расширенным JQL поиском. Например функция linkedIssuesOf перестает работать с такой ошибкой:
При случайном удалении из админки не приехавшего линка, а того который был в jira изначально, все связи сделанные в задачах с этим линком так же удаляются из задач. Чтобы восстановить такие связи можно использовать скрипт приведенный в листинге ниже.
При удалении линка об этом делается запись в историю каждой задачи - за это и зацепимся. Хоть затрагивается и большое количество задач и история линковок может содержать не относящиеся к удалению типы линков, окно записей при удалении линка в историю небольшое, зададим в JQL все затронутые задачи а все остальные линковки фильтранём по признаку непопадания в окно задав startTS и endTS. Так же учтем направление линковки и можно запускать скрипт.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.link.IssueLinkService
import com.atlassian.jira.bc.issue.link.IssueLinkService.IssueLinkValidationResult
import com.atlassian.jira.util.ErrorCollection
def issueManager = ComponentAccessor.getIssueManager()
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
String ALL_TASK_JQL = '''issue IN updatedBy("userwhodeletedtype") and updated >= "2021/12/03 00:20")'''
def runJQL(String jql, ApplicationUser user) {
def issuesSearch
def searchService = ComponentAccessor.getComponent(SearchService.class)
def issueManager = ComponentAccessor.getIssueManager()
def parseJQLResult = searchService.parseQuery(user, jql)
if (parseJQLResult.isValid()) {
def result = searchService.search(user, parseJQLResult.getQuery(), PagerFilter.getUnlimitedFilter())
def documentIssues = result.results
return documentIssues.collect { issueManager.getIssueObject(it.id) }
} else {
log.error("Invalid JQL: " + jql);
log.debug("SendRFCReportToConfluenceSRV out")
}
return issuesSearch
}
Timestamp startTS = new Timestamp(1638480000000L)
Timestamp endTS = new Timestamp(1638481800000L)
def arrayIssues = runJQL(ALL_TASK_JQL, currentUser) as List<Issue>
for (issue in arrayIssues) {
def thisIssueIsBlockedBy = ComponentAccessor.changeHistoryManager.getChangeItemsForField(issue, "Link")
.findAll{it.created > startTS && it.created < endTS && it.getFromString()?.contains("This issue is blocked by ") }
?.collect{it?.from}?.collect{issueManager.getIssueObject(it)}
if (thisIssueIsBlockedBy) thisIssueIsBlockedBy.collect{createLink(it, issue, 11000L, 0, currentUser)}
def thisIssueIsBlocks = ComponentAccessor.changeHistoryManager.getChangeItemsForField(issue, "Link")
.findAll{it.created > startTS && it.created < endTS && it.getFromString()?.contains("This issue blocks ")}
?.collect{it?.from}?.collect{issueManager.getIssueObject(it)}
if (thisIssueIsBlocks) thisIssueIsBlocks.collect{createLink(issue, it, 11000L, 0, currentUser)}
}
void createLink(Issue fromIssue, Issue toIssue, Long issueLinkTypeId, Long sequence, ApplicationUser user) {
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
issueLinkManager.createIssueLink(fromIssue.id, toIssue.id, issueLinkTypeId, sequence, user)
}