Snippets
Matthew Clark (Adaptavist) Script Listener (ISSUE UPDATED ONLY) - Push Custom Field Value updated to Parent-Child Linked issues. (Not recursive)
Created by
Matthew Clark
last modified
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.changehistory.ChangeHistoryManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.user.ApplicationUser
import org.apache.log4j.Logger
import org.apache.log4j.Level
import java.text.SimpleDateFormat
def log = Logger.getLogger(getClass())
log.setLevel(Level.DEBUG)
/**
*
*
* Use as a listener with the "ISSUE UPDATED" event and this currently copies a standard Text field value.
*
* In portfolio we have a parent link and also the concept of child issues
*
* If we want to sync field values between the parent and child when an issue is updated we can use a listener that
* triggers on the issue updated event and performs several other checks.
*
* 1 - Check if the issue that was updated has portfolio links to either a parent or a child
* 2 - Check that the field we want to sync the value for was updated on this issue update event
* 3 - Find the relevant link types to either the parent or the child and then push the updated field value to the linked issue
* 4 - Make sure we only update the issues linked under a given link type which by default for Portfolio is Parent-Child Link
*
* The link details used by portfolio parent - child issues as shown in database on my test server
* SQL if you need to look for yourself: SELECT * FROM issuelinktype
*
* [ID:10201, LINKNAME:Parent-Child Link, INWARD:is child of, OUTWARD:is parent of, PSTYLE:jira_jpos_parent_child]
*
* Limitation Note:
* I have coded this to push updates from parent to children and also when a child is updated it will push the update to the parent.
* HOWEVER when child updates the parent this script will NOT then push the update to all the other children under the parent
* because I do not fire the issue updated event when the parent is altered by this script.
*
* It might be posibble to just fire the issue updated event when updating the parent so that then triggers this same script to
* then update all childen under that parent but I dont want to do this here as that could cause loops and crash Jira.
*
* Feel free to try this yourselves but I will not do this in this script as I do not want to break peoples Jira installs
*
*/
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
Issue issue = event.issue as Issue
CustomField myField = customFieldManager.getCustomFieldObject("customfield_10013")
if(! myField){
log.debug("The Field you are looking for does not exist, check the id")
return
}
Boolean hasFieldChanged = checkFieldHasBeenChanged(issue,myField)
if(hasFieldChanged){
log.debug("Field Change Detected so Will copy to Portfolio Child Or Parent if the link is found")
pushUpdateToPortfolioLinkedIssues(issue,myField)
}
/**
*
* @param issue - Issue that triggered Listener
* @param customField - customField object we want to check if it was changed on this update
* @return boolean true if the field value was updated
*
* Notes: this method could probably be improved. I get the Timestamps for the issue that was updated
* and the create time for the change history item of the field change. Then convert them to
* the time format "dd-M-yyyy hh:mm:ss" to make it so it only goes to the second instead of the
* micro second as there is a chance the times will be different in micro seconds but should be
* the same second. If you have a slower system that then you may need to change the format so
* it only goes to the minute.
* Or implement a time interval difference that is acceptable and allow for that in your Timestamp
* comparisons
*
*/
Boolean checkFieldHasBeenChanged(Issue issue, CustomField customField){
def log = Logger.getLogger(getClass())
log.setLevel(Level.DEBUG)
ChangeHistoryManager changeHistoryManager = ComponentAccessor.getChangeHistoryManager()
String fieldName = customField.getName()
log.debug("FieldName = " + fieldName)
ChangeItemBean changeItem = changeHistoryManager.getChangeItemsForField(issue,fieldName).last()
// we will compare change issue update and field update times to the second, change the format if you want it to be to the minute
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-M-yyyy hh:mm:ss")
String issueUpdateTime = simpleDateFormat.format(issue.updated)
String fieldUpdateTime = simpleDateFormat.format(changeItem.created)
//defualt value is timestamp like 2019-09-14 20:18:16.823 and the value after the . can differ very slightly for the time the
//issue was updated versus the time the changehistory was updated.
log.debug("""
issueUpdatedTime = $issueUpdateTime
fieldUpdateTime = $fieldUpdateTime
Comparison Logic check: ${issueUpdateTime == fieldUpdateTime}""")
// if the update time of the field and the issue are to the same second then we assume the field was changed
// i do not check the actualy values changed. You would need to get the last change item and the one before to compare this
return issueUpdateTime == fieldUpdateTime
}
/**
*
* @param issue - Issue that triggered Listener
* @param fieldToCopy - CustomField Object for the field you want to update
*/
void pushUpdateToPortfolioLinkedIssues(Issue issue, CustomField fieldToCopy){
def log = Logger.getLogger(getClass())
log.setLevel(Level.DEBUG)
String linkTypeName = "Parent-Child Link"
def currentIssuesFieldValue = issue.getCustomFieldValue(fieldToCopy)
if(currentIssuesFieldValue) {
IssueLinkTypeManager issueLinkTypeManager = ComponentAccessor.getComponent(IssueLinkTypeManager)
IssueLinkManager issueLinkManager = ComponentAccessor.getIssueLinkManager()
IssueManager issueManager = ComponentAccessor.getIssueManager()
ApplicationUser user = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser()
def linkTypes = issueLinkTypeManager.getIssueLinkTypes(false)
Long parentChildPortfolioLinkID = linkTypes.find { linkType ->
linkType.name == linkTypeName
}?.id
Collection<IssueLink> parentToChildLinks = issueLinkManager.getInwardLinks(issue.id)
Collection<IssueLink> childToParentLinks = issueLinkManager.getOutwardLinks(issue.id)
//Process Children issues and push field update to child issues
parentToChildLinks.each { linkObject ->
if (linkObject.linkTypeId == parentChildPortfolioLinkID) {
def childIssue = linkObject.sourceObject as MutableIssue
if (childIssue) {
log.debug("Dest Issue Key = ${childIssue.key}")
def destIssueCurrentFieldValue = childIssue.getCustomFieldValue(fieldToCopy)
////detect if destination issue has value already, if the values are already the same, do nothing
if (currentIssuesFieldValue == destIssueCurrentFieldValue) {
log.debug("Values already the same")
} else {
childIssue.setCustomFieldValue(fieldToCopy, currentIssuesFieldValue)
issueManager.updateIssue(user, childIssue, EventDispatchOption.DO_NOT_DISPATCH, false)
}
}
}
}
//Process Parent Issues and push field update to parent issues
childToParentLinks.each { linkObject ->
if (linkObject.linkTypeId == parentChildPortfolioLinkID) {
def parentIssue = linkObject.destinationObject as MutableIssue
if (parentIssue) {
log.debug("Dest Issue Key = ${parentIssue.key}")
def destIssueCurrentFieldValue = parentIssue.getCustomFieldValue(fieldToCopy)
////detect if destination issue has value already, if the values are already the same, do nothing
if (currentIssuesFieldValue == destIssueCurrentFieldValue) {
log.debug("Values already the same")
} else {
parentIssue.setCustomFieldValue(fieldToCopy, currentIssuesFieldValue)
issueManager.updateIssue(user, parentIssue, EventDispatchOption.DO_NOT_DISPATCH, false)
}
}
}
}
}else {
log.debug("The current Issue ${issue.key} does not have a value in the chosen custom field ${fieldToCopy.id}")
}
}
|
Comments (0)
You can clone a snippet to your computer for local editing. Learn more.