Use case 1) If I only change the request type, everything works and all adjustments are carried out by the script as desired.
2) However, if the issue is to be moved to another project, it does not always work. Here I emphasise: "Not always".
Because sometimes it does work.
Problem: Unfortunately, I don't know why it sometimes works and then not.
Maybe you can help me.
package MoveClass
import com.atlassian.crowd.embedded.api.Group
import com.atlassian.jira.util.ErrorCollection
import com.atlassian.jira.issue.IssueVerifier
import com.atlassian.jira.issue.history.ChangeLogUtils
import com.atlassian.jira.event.issue.IssueEventBundle
import org.ofbiz.core.entity.GenericValue
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem
import com.atlassian.jira.issue.fields.OrderableField
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.config.ConstantsManager
import com.atlassian.jira.event.issue.IssueEventManager
import com.atlassian.jira.project.Project
import com.atlassian.jira.config.IssueTypeManager
import com.atlassian.jira.transaction.Transaction
import com.atlassian.jira.transaction.Txn
import com.atlassian.jira.event.type.EventType
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.AttachmentManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.fields.FieldManager
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.issuetype.IssueType
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueUpdateBean
import com.atlassian.jira.issue.status.Status
import com.atlassian.jira.workflow.WorkflowManager
import com.atlassian.jira.workflow.JiraWorkflow
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.bean.MoveIssueBean
import com.atlassian.servicedesk.api.request.type.CustomerRequestType
import com.atlassian.servicedesk.api.request.type.CustomerRequestTypeQuery
import com.atlassian.servicedesk.api.request.type.CustomerRequestTypeService
import com.atlassian.servicedesk.api.requesttype.RequestType
import com.atlassian.servicedesk.api.requesttype.RequestTypeQuery
import com.atlassian.servicedesk.api.requesttype.RequestTypeService
import com.atlassian.servicedesk.api.user.UncheckedUser
import com.atlassian.servicedesk.api.user.UserFactory
import com.atlassian.servicedesk.api.util.paging.PagedRequest
import com.atlassian.servicedesk.api.util.paging.PagedResponse
import com.atlassian.servicedesk.api.util.paging.SimplePagedRequest
import com.atlassian.servicedesk.api.user.UserFactory
import com.atlassian.jira.event.issue.IssueEventBundleFactory
import groovy.util.logging.Log4j
import java.util.regex.Matcher
import java.util.regex.Pattern
@Log4j
public class Move {
// String requestTypeName - Name of the desired request Type
// boolean sameStatusIfNoMapping
// Issue issue
// String targetProjectKey
// String targetIssueType
public static String move(String requestTypeName, boolean sameStatusIfNoMapping, Issue issue, String targetProjectKey, String targetIssueType) throws Exception {
boolean debug = true
if(debug) {
log.warn(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Start move issue")
log.warn("sameStatusIfNoMapping:" + sameStatusIfNoMapping)
log.warn("Issue: " + issue)
log.warn("targetProjectKey: " + targetProjectKey)
log.warn("targetIssueType: " + targetIssueType)
}
IssueManager issueManager = ComponentAccessor.getIssueManager()
IssueTypeManager issueTypeManager = ComponentAccessor.getComponent(com.atlassian.jira.config.IssueTypeManager.class)
WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager()
JiraWorkflow workflowOriginal = workflowManager.getWorkflow(issue)
MutableIssue mIssueOriginal = issueManager.getIssueObject(issue.getId() as Long)
Project projectTarget = null
try{
projectTarget = Projects.getByKey(targetProjectKey)
}catch(Exception e) {
throw new Exception (e.getMessage())
}
IssueType issueTypeTarget = issueTypeManager.getIssueTypes().find{ it.name == targetIssueType }
if (issueTypeTarget == null) {
throw new Exception ("No issue type with the name $targetIssueType where found.")
}
JiraWorkflow workflowTarget = workflowManager.getWorkflow(projectTarget.getId(), issueTypeTarget.getId())
if(debug) log.warn("workflowTarget (95): " + workflowTarget + " || projectTarget: " + projectTarget.getId() + " issueTypeTarget id: " + issueTypeTarget.getId())
// try catch new Exception throw
// if (sameStatusIfNotInMapping && _targetWf.getLinkedStatusObjects().any { it.name.equalsIgnoreCase(originalIssueObject.status.name) }) {
// return originalIssueObject.status.name
// }
ApplicationUser currentLoggedInUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
AttachmentManager attachmentManager = ComponentAccessor.getAttachmentManager()
IssueEventManager issueEventManager = ComponentAccessor.getIssueEventManager()
IssueEventBundleFactory issueEventBundleFactory = ComponentAccessor.getComponent(com.atlassian.jira.event.issue.IssueEventBundleFactory.class)
FieldManager fieldManager = ComponentAccessor.getFieldManager()
FieldLayoutManager fieldLayoutManager = ComponentAccessor.getFieldLayoutManager()
ConstantsManager constantManager = ComponentAccessor.getConstantsManager()
MutableIssue mIssueTarget = issueManager.getIssueObject(mIssueOriginal.getId())
Project projectOriginal = issue.getProjectObject()
IssueType issueTypeOriginal = issue.getIssueType()
Status statusOriginal = issue.getStatus()
Status statusTarget = workflowTarget.getLinkedStatusObjects().find { it.name.equalsIgnoreCase(status_to) }
if (statusTarget == null) {
statusTarget = workflowTarget.getLinkedStatusObjects().first()
}
def getWorkflowForType = {Long pid, String itId ->
workflowManager.getWorkflow(pid, itId)
}
def isWorkflowMatch = {String currentIssueTypeId, String issueTypeIdTarget ->
return getWorkflowForType(projectOriginal.getId(), issueTypeOriginal.getId()).equals(getWorkflowForType(projectTarget.getId(), issueTypeIdTarget))
}
def migrateIssueToWorkflow = {MutableIssue issueToMigrate, JiraWorkflow targetWorkflow, Status targetStatus ->
workflowManager.migrateIssueToWorkflow(issueToMigrate, targetWorkflow, targetStatus)
}
MoveIssueBean moveIssueBean = new MoveIssueBean(constantManager, ComponentAccessor.getProjectManager())
moveIssueBean.getFieldValuesHolder().put("project", projectTarget.getId())
moveIssueBean.getFieldValuesHolder().put("issuetype", issueTypeTarget.getId())
moveIssueBean.setCurrentStep(workflowOriginal.getLinkedStep(issue.getStatus()).getId())
moveIssueBean.setSourceIssueKey(issue.getKey())
moveIssueBean.setTargetStatusId(statusTarget.getId())
moveIssueBean.setUpdatedIssue(mIssueTarget)
if(debug) log.warn("moveIssueBean (141):" + moveIssueBean)
def moveIssueDetails = { MutableIssue issueNew, String oldKey, String wfOriginalId ->
if(debug) log.warn("moveIssuesDetails begin 143:")
DefaultIssueChangeHolder changeHolder = new DefaultIssueChangeHolder()
//IssueType issueTypeCurrent = constantManager.getIssueType(issueTypeOriginal.getId())
//IssueType issueTypeTargetForMoveIssueDetail = constantManager.getIssueType(issueTypeTarget.getId())
String newKey = issueNew.getKey()
if(debug) log.warn("issueNew key (150): " + newKey)
if(!projectOriginal.equals(projectTarget)) {
if(debug) log.warn("!projectOriginal.equals(projectTarget) (152)")
changeHolder.addChangeItem(new ChangeItemBean("jira", "project", projectOriginal.getId().toString(), projectOriginal.getName(), projectTarget.getId().toString(), projectTarget.getName()))
changeHolder.addChangeItem(new ChangeItemBean("jira", "Key", (String)null, oldKey, (String)null, newKey));
}
if(!issueTypeOriginal.equals(issueTypeTarget)) { // isSubTask() fehlt - Zeile 314
if(debug) log.warn("!issueTypeOrigininal.equals(issueTypeTarget) (158): ${issueTypeOriginal} equals ${issueTypeTarget} ?")
changeHolder.addChangeItem(new ChangeItemBean("jira", "issuetype", issueTypeOriginal.getId(), issueTypeOriginal.getName(), issueTypeTarget.getId(), issueTypeTarget.getName()))
issueNew.setIssueTypeObject(issueTypeTarget)
if(debug) {
log.warn("issueTypeTarget: " + issueTypeTarget)
log.warn("issueNew.setIssueTypeObject: " + issueNew)
}
}
if(!isWorkflowMatch(issueTypeOriginal.getId(), issueTypeTarget.getId())) {
if(debug) log.warn("!isWorkflowMatch(issueTypeOriginal.getId() (168) --> ${issueTypeOriginal.getId()}, issueTypeTarget.getId()) --> ${issueTypeTarget.getId()} (166)")
changeHolder.addChangeItem(new ChangeItemBean("jira", "Workflow", wfOriginalId, workflowManager.getWorkflow(issue).getName(), issueNew.getWorkflowId().toString(), getWorkflowForType(projectTarget.getId(), issueTypeTarget.getName()).getName()))
if(!statusOriginal.equals(statusTarget)) {
if(debug) log.warn("!statusOriginal.equals(statusTarget) (171) ${statusOriginal} == ${statusTarget}")
changeHolder.addChangeItem(new ChangeItemBean("jira", "status", statusOriginal.getId(), statusOriginal.getName(), statusTarget.getId(), statusTarget.getName()))
}
}
// Set request type on issue
RequestType requestType = findRequestType(requestTypeName)
CustomerRequestTypeService customerRequestTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(CustomerRequestTypeService)
CustomerRequestTypeQuery customerRequestTypeQuery = customerRequestTypeService.newQueryBuilder().requestType(requestType).build()
UserFactory userFactory = ComponentAccessor.getOSGiComponentInstanceOfType(UserFactory)
UncheckedUser uncheckedUser = userFactory.getUncheckedUser()
log.warn("uncheckedUser: (184) ${uncheckedUser}")
CustomerRequestType customerRequestType = customerRequestTypeService.getCustomerRequestType(uncheckedUser, customerRequestTypeQuery) as CustomerRequestType
CustomFieldManager cfm = ComponentAccessor.getCustomFieldManager()
// Customfields that are to be filled or changed:
// cfCRT: Customer Request Type
// cfNewFLag: New Information (Option field)
// cfAssignedGroup: Group Picker field.
CustomField cfCRT = cfm.getCustomFieldObject(15403)
CustomField cfNewFlag = cfm.getCustomFieldObject(15513)
CustomField cfAssignedGroup = cfm.getCustomFieldObject(10238)
def collCurrentIssue = (Collection) issue.getCustomFieldValue(cfNewFlag)
if (collCurrentIssue == null) {
def cfConfigCurrentIssue = cfNewFlag.getRelevantConfig(issue)
def optionsCurrentIssue = ComponentAccessor.getOptionsManager().getOptions(cfConfigCurrentIssue)
def optionsToSet = optionsCurrentIssue.findAll { it.value == "Yes" }
issueNew.setCustomFieldValue(cfNewFlag, optionsToSet)
log.warn("setCustomFieldValue (197) ${cfNewFlag}")
}
// Set assigned group on issue
Group group = identifyAssignedGroup(issueNew, requestType.getId())
log.warn("identifyAssignedGroup (202): Group: ${group?.getName()}")
issueNew.setAssignee(null)
issueNew.setCustomFieldValue(cfCRT, customerRequestType)
issueNew.setCustomFieldValue(cfAssignedGroup, [group])
log.warn("setCustomFieldValue (206) ${cfAssignedGroup}")
issueManager.updateIssue(currentLoggedInUser, issueNew, EventDispatchOption.DO_NOT_DISPATCH, false) // statt issueNew.store()
if(debug) {
log.warn("issueManage.updateIssue (210): currentLoggedInUser: " + currentLoggedInUser.getDisplayName())
log.warn("IssueNew key (211): " + issueNew.getKey())
}
Map<String, ModifiedValue> modifiedFields = issueNew.getModifiedFields()
Iterator iterator = modifiedFields.keySet().iterator()
if(debug) log.warn("modifiedFields (216): ${modifiedFields} --> while iterator.hasNext():")
while(iterator.hasNext()) {
String fieldId = (String) iterator.next()
if(debug) log.warn("fieldId (220):" + fieldId)
if(fieldManager.isOrderableField(fieldId)) {
if(debug) log.warn("isOrderableField (222):" + fieldManager.isOrderableField(fieldId))
OrderableField field = fieldManager.getOrderableField(fieldId)
if(debug) log.warn("OrderableField (224):" + field)
FieldLayoutItem fieldLayoutItem = fieldLayoutManager.getFieldLayout(issueNew).getFieldLayoutItem(field)
field.updateValue(fieldLayoutItem, issueNew, (ModifiedValue) modifiedFields.get(fieldId), changeHolder)
if(debug) log.warn("field.updateValue (227)")
}
}
issueNew.resetModifiedFields()
if(debug) log.warn("issueNew.resetModifiedFields (232):" + issueNew)
return changeHolder
} // End moveIssueDetails
def dispatchIssueUpdateEvents = { Issue issueNew, GenericValue updateLog, DefaultIssueChangeHolder issueChangeHolder ->
if(debug) log.warn("dispatchIssueUpdateEvents begin: 237")
if(updateLog != null && !issueChangeHolder.getChangeItems().isEmpty()) {
Long eventTypeId;
if(debug) log.warn("!projectOriginal.equals(projectTarget) (241): ${projectOriginal} equals ${projectTarget}?")
if(!projectOriginal.equals(projectTarget)) {
eventTypeId = EventType.ISSUE_MOVED_ID
if(debug) log.warn("EventType:ISSUE_MOVED_ID")
} else {
eventTypeId = EventType.ISSUE_UPDATED_ID
if(debug) log.warn("EventType.ISSUE_UPDATED_ID")
}
if(debug) log.warn("eventTypeid: " + eventTypeId)
issueEventManager.dispatchRedundantEvent(eventTypeId, issueNew, currentLoggedInUser, updateLog, true, issueChangeHolder.isSubtasksUpdated())
if(debug) log.warn("issueEventManage.dispatchRedundantEvent (252)")
//def _issueChangeHolderTransformerClazz = ComponentAccessor.getClassLoader().loadClass("com.atlassian.jira.issue.util.transformers.IssueChangeHolderTransformer")
IssueUpdateBean issueUpdateBean = new IssueUpdateBean(issueNew, issue, eventTypeId, currentLoggedInUser)
if(debug) log.warn("issueUpdateBean (256)")
IssueEventBundle issueEventBundle = issueEventBundleFactory.createIssueUpdateEventBundle(issueNew, updateLog, issueUpdateBean, currentLoggedInUser)
issueEventManager.dispatchEvent(issueEventBundle)
if(debug) log.warn("issueEventManager.dispatchEvent (259)")
}
} // End dispatchIssueUpdateEvents
def moveIssueInTxn = {Transaction txn, MutableIssue originalIssueObject1, MutableIssue updatedIssueObject, String originalWfId ->
if(debug) log.warn("moveIssueInTxn begin 264:")
if(!isWorkflowMatch(issueTypeOriginal.getId(), issueTypeTarget.getId())) {
if(debug) log.warn("!isWorkflowMatch (266): issueTypeOriginal.getId(): ${issueTypeOriginal.getId()} issueTypeTarget ID: ${issueTypeTarget.getId()}")
migrateIssueToWorkflow(updatedIssueObject, workflowManager.getWorkflow(projectTarget.getId(), issueTypeTarget.getId()), statusTarget)
if(debug) log.warn("migrateIssueToWorkflow...")
}
updatedIssueObject.setUpdated(new java.sql.Timestamp(System.currentTimeMillis()))
if(debug) log.warn("updateIssueObject.setUpdated (272)" + updatedIssueObject)
def issueChangeHolder = moveIssueDetails(updatedIssueObject, originalIssueObject1.getKey(), originalWfId);
if(debug) log.warn("issueChangeHolder (274) = moveIssueDetails: ${updatedIssueObject}, originalIssueObject1.getKey(): ${originalIssueObject1.getKey()}, ${originalWfId}")
GenericValue updateLog = ChangeLogUtils.createChangeGroup(currentLoggedInUser, originalIssueObject1.getGenericValue(), updatedIssueObject.getGenericValue(), issueChangeHolder.getChangeItems(), false);
if(debug) log.warn("updatelog (276):" + updateLog)
// Set info that issue were moved.
if(projectOriginal.equals(projectTarget)) {
if(debug) log.warn("projectOriginal.equals(projectTarget) (280): ${projectOriginal} equals ${projectTarget} ")
issueManager.recordMovedIssueKey(originalIssueObject1)
if(debug) log.warn("issueManager.recordMovedIssueKey(originalIssueObject1) (282): ${originalIssueObject1}")
}
txn.commit()
dispatchIssueUpdateEvents(updatedIssueObject, updateLog, issueChangeHolder)
if(debug) log.warn("dispatchIssueUpdateEvents: (286): ${updatedIssueObject}, UpdateLog: ${updateLog}, ${issueChangeHolder}")
} // End moveIssueInTxn
String sourceIssueKey = moveIssueBean.getSourceIssueKey()
if(debug) log.warn("sourceIssueKey (290): ${sourceIssueKey}")
if(!issue.getKey().equals(sourceIssueKey)) {
throw new Exception("An error occured in line 292. issue key (${issue.getKey()}) is equal source key (${sourceIssueKey})")
} else {
String originalWfId = null
MutableIssue updatedIssueObject = moveIssueBean.getUpdatedIssue()
if(debug) log.warn("originalWfId (296): " + originalWfId + " updatedIssueObject: " + updatedIssueObject)
if (updatedIssueObject.getWorkflowId() != null) {
originalWfId = updatedIssueObject.getWorkflowId().toString()
if(debug) log.warn("updatedIssueObject.getWorkflowId() != null (299) - originalWfId: " + originalWfId)
}
IssueVerifier issueVerifier = new IssueVerifier()
Map<String, String> workflowMigrationMapping = new HashMap()
workflowMigrationMapping.put(updatedIssueObject.getStatus().getId(), statusTarget.getId())
if(debug) log.warn("workflowMigrationMapping.put( (305) ${updatedIssueObject.getStatus().getId()}, ${statusTarget.getId()})")
ErrorCollection errorCollection = issueVerifier.verifyIssue(issue, workflowMigrationMapping, true)
if (errorCollection != null && errorCollection.hasAnyErrors()) {
//addErrorCollection(errorCollection)
if(debug) log.warn("errorCollection: " + errorCollection)
throw new Exception("An workflow migration error occured.")
} else {
def txnClass = ComponentAccessor.getClassLoader().loadClass("com.atlassian.jira.transaction.Txn")
def txn = Txn.begin()
if(!projectOriginal.equals(projectTarget)) {
if(debug) log.warn("!projectOriginal.equals(projectTarget) ${projectOriginal}, equals ${projectTarget}")
Project pTarget = projectTarget
updatedIssueObject.setProjectObject(pTarget)
if(debug) log.warn("updatedIssueObject.setProjectObject ${pTarget}")
long incCount = ComponentAccessor.getProjectManager().getNextId(pTarget)
if(debug) log.warn("incCount (322): ${incCount}")
updatedIssueObject.setNumber(Long.valueOf(incCount))
attachmentManager.moveAttachments(issue, updatedIssueObject.getKey())
if(debug) log.warn("attachmentManager.moveAttachments ()...")
}
try{
moveIssueInTxn(txn, mIssueOriginal, updatedIssueObject, originalWfId)
if(debug) log.warn("moveIssueInTxn (329)")
} finally {
txn.finallyRollbackIfNotCommitted()
}
return mIssueOriginal.getKey()
}
}
} // End public static move
/****************************************************
* Determines the request type from the selection.
*
* @param name as String - Name of the desired request type.
* @return requestType as RequestType
****************************************************/
static RequestType findRequestType(String name) {
int numberOfRequestTypes = 300 //approximate amount of customer request types on your instance the more the better)
RequestType newRequestType = null
RequestTypeService requestTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(RequestTypeService)
for(int limitRequest = 0; limitRequest <numberOfRequestTypes; limitRequest += 100){
PagedRequest pagedRequest = new SimplePagedRequest(limitRequest,limitRequest+100)
RequestTypeQuery reqTypeQuery = requestTypeService.newQueryBuilder().pagedRequest(pagedRequest).build()
PagedResponse<RequestType> reqTypes = requestTypeService.getRequestTypes(ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), reqTypeQuery)
if(reqTypes.results.find{ it.getName() == name }){
newRequestType = reqTypes.results.find{it.getName() == name}
break
}
}
return newRequestType
}
/****************************************************
* Determines the assigned group from the moved issue and the request type.
*
* @param mIssue as MutableIssue
* @param requestTypeId as int
* @return assigned group as Group
****************************************************/
static Group identifyAssignedGroup(MutableIssue mIssue, int requestTypeId) {
// I have simplified it here. Because we are specifically determining the AD groups.
// Please add your own groupname
GroupManager groupManager = ComponentAccessor.getGroupManager()
Group group = groupManager.getGroup("ad-group-name")
return group
}
} // End class
I would be very grateful for your help.