Skip to content

Instantly share code, notes, and snippets.

@hermannschwaerzler
Last active June 29, 2018 13:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hermannschwaerzler/ecc1883bda7bc06b6e34 to your computer and use it in GitHub Desktop.
Save hermannschwaerzler/ecc1883bda7bc06b6e34 to your computer and use it in GitHub Desktop.
JIRA: move issue programmatically (in groovy; supported by scriptrunner plugin). In Jira 4 and 5 storing the changed issue has to be done in a new thread. Everything wrapped up into a utility class. Still missing: Thorough testing as well as error and exception handling
import com.atlassian.jira.ComponentManager
import com.atlassian.crowd.embedded.api.User
import com.atlassian.crowd.model.user.UserTemplate
import com.atlassian.jira.user.preferences.PreferenceKeys
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.project.Project
import org.ofbiz.core.entity.*;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.history.ChangeLogUtils
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.util.AttachmentUtils
import com.atlassian.jira.event.issue.IssueEventDispatcher
import com.atlassian.jira.event.type.EventType
import java.io.File
class UIBKUtils {
static ComponentManager componentManager = ComponentManager.getInstance()
def static indexManager = componentManager.getIndexManager()
def static projectManager = componentManager.getProjectManager()
def static issueManager = componentManager.getIssueManager()
// ...
public static void moveIssueToProject(MutableIssue currentIssue, Project targetProject, boolean dispatchEvent = true) {
GenericDelegator delegator = GenericDelegator.getGenericDelegator("default")
Long issueId = currentIssue.getId()
GenericValue originalIssueGV = delegator.findByPrimaryKey(makeIssuePk(delegator, issueId))
String oldKey = currentIssue.getKey()
def newIssueNumber = projectManager.getNextId(targetProject)
String newKey = targetProject.getKey() + "-" + newIssueNumber
Long newProjectId = targetProject.getId()
Project currentProject = currentIssue.getProjectObject()
// do nothing if target project is the same as source project
Long currentProjectId = currentProject.getId()
if (newProjectId == currentProjectId) {
return
}
moveAttachments(issueId, newKey, targetProject)
GenericValue updatedIssueGV = delegator.findByPrimaryKey(makeIssuePk(delegator, issueId))
updatedIssueGV.setString("key", newKey)
updatedIssueGV.setString("project", newProjectId.toString())
IssueChangeHolder changeHolder = new DefaultIssueChangeHolder()
changeHolder.addChangeItem(
new ChangeItemBean(
ChangeItemBean.STATIC_FIELD,
"Project",
currentProject.getId().toString(),
currentProject.getName(),
targetProject.getId().toString(),
targetProject.getName()
)
)
changeHolder.addChangeItem(
new ChangeItemBean(
ChangeItemBean.STATIC_FIELD,
"Key",
null,
oldKey,
null,
newKey
)
)
User currentUser = componentManager.getJiraAuthenticationContext().getUser()
GenericValue updateLog = ChangeLogUtils.createChangeGroup(
currentUser,
originalIssueGV,
updatedIssueGV,
changeHolder.getChangeItems(),
false)
// store updated issue in a different thread
try {
int rowsAffected
def th = Thread.start {
sleep 50
rowsAffected = delegator.store(updatedIssueGV)
indexManager.reIndex(updatedIssueGV)
if (dispatchEvent) {
Issue newIssue = issueManager.getIssueObject(issueId)
dispatchIssueMovedEvent(newIssue, updateLog, changeHolder, currentUser)
}
}
} catch(Exception e) {
// ...
}
}
private static GenericPK makeIssuePk(GenericDelegator delegator, Long id) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
return delegator.makePK("Issue", map);
}
private static void moveAttachments(Long issueId, String newIssueKey, Project targetProject) {
def attachmentManager = componentManager.getAttachmentManager()
def attachmentPathManager = componentManager.getAttachmentPathManager()
Issue issue = issueManager.getIssueObject(issueId)
String fSep = System.getProperty("file.separator")
List<Attachment> attachments = attachmentManager.getAttachments(issue)
attachments.each{
attachment ->
File attachmentFile = AttachmentUtils.getAttachmentFile(attachment)
String fileName = attachmentFile.getName()
String originalFilePath = attachmentFile.getAbsolutePath();
String targetDirectoryPath =
attachmentPathManager.getAttachmentPath() + fSep +
targetProject.getKey() + fSep +
newIssueKey;
if (attachmentFile.exists()) {
File destDirectory = new File(targetDirectoryPath)
destDirectory.mkdirs()
attachmentFile.renameTo(new File(destDirectory, fileName))
} else {
// ...
}
}
}
// generate "Issue Moved" event
private static void dispatchIssueMovedEvent(Issue newIssue, GenericValue updateLog, IssueChangeHolder issueChangeHolder, User currentUser)
throws GenericEntityException {
if ( updateLog != null && !issueChangeHolder.getChangeItems().isEmpty() ) {
Long eventTypeId = EventType.ISSUE_MOVED_ID
IssueEventDispatcher.dispatchEvent(
eventTypeId,
newIssue,
currentUser,
updateLog,
true,
issueChangeHolder.isSubtasksUpdated()
)
}
}
}
@WCORHIO
Copy link

WCORHIO commented Sep 14, 2015

Hermann,
In searching answers.atlassian.com your name appeared often and I am hoping you can help me. I am new to JIRA and I have been tasked with modifying a workflow to move issues from one project to another. The goal is to have a capacity to move issues to a 'holding' project if a major delay is encountered. From what I have read I will need to setup a screen for a transition where the user will specify the new project then programmatically move the issue based on the information the user entered. Am I correct in this approach? After reading the various answers/discussions I am not sure if moving issues between projects is possible in the workflow. Is it possible to move issues between projects in the workflow? If it is possible can you give me some guidance on how best to implement this or direct me to resources that could help me? We are running version 6.3.15. Thank you for any help you can provide.
Peace,
Will

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment