I am writing a script in Jira using ScriptRunner to transition our Epic based on the status of its child tasks I.E Story, Task, Sub Task. I believe I have my script the way I want it but my version of Jira 8.1.0 is not allowing me to utilize issueService or WorkflowTransitionUtil. I keep getting this error. I am in the process of moving us over to a newer version of Data Center but we are currently on a old standalone server version. My system info tells me i am using 8.1.0 so I believe IssueService and WorkflowTransitionUtil should work.
Here is the error I am getting:
2024-08-20 16:32:17,478 ERROR [runner.ScriptBindingsManager]: Failed to transition Epic TESTGLE-3 to Design (SDP): No such property: issueService for class: Script398
I just feel it has something to do with my imports and maybe this outdated version possibly.
Here is my Script:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.workflow.IssueWorkflowManager
import com.atlassian.jira.workflow.WorkflowManager
import com.atlassian.jira.workflow.TransitionOptions
import org.apache.log4j.Logger
// Logger for debugging
def log = Logger.getLogger("com.example.TransitionScript")
// Initialize managers and components
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issueService = ComponentAccessor.getIssueService()
def workflowManager = ComponentAccessor.getWorkflowManager()
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
// Get the issue and its type
def issue = event.issue as MutableIssue
def issueType = issue.issueType.name
// Get the Epic Link custom field
def epicLinkField = customFieldManager.getCustomFieldObjectByName("Epic Link")
// Check if the issue is a Story, Task, or Sub-task and has an Epic Link
if (["Story", "Task", "Sub-task"].contains(issueType) && issue.getCustomFieldValue(epicLinkField)) {
def epicLink = issue.getCustomFieldValue(epicLinkField) as Issue
if (epicLink) {
// Get all child issues linked to the Epic
def childIssues = issueLinkManager.getOutwardLinks(epicLink.id)
.findAll { it.issueLinkType.name == "Epic-Story Link" }
.collect { it.destinationObject }
// Get the status of all child issues
def allStatuses = childIssues.collect { it.status.name }
// Determine the lowest status
def lowestStatus = allStatuses.min { status -> getStatusOrder(status) }
// Transition the Epic to the lowest status
if (lowestStatus) {
transitionEpicToStatus(epicLink, lowestStatus)
}
}
}
// Helper function to map statuses to their order
def getStatusOrder(String status) {
def statusMap = [
"To Do (SDP)": 1,
"Design (SDP)": 2,
"Design Hold (SDP)": 3,
"Design Review (SDP)": 4,
"Working (SDP)": 5,
"Working Hold (SDP)": 6,
"Development Testing (SDP)": 7,
"DevTest Hold (SDP)": 8,
"Code Review (SDP)": 9,
"CR Hold (SDP)": 10,
"Integration Testing (SDP)": 11,
"Integration Hold (SDP)": 12,
"LWR in Progress (SDP)": 13,
"LWR Hold (SDP)": 14,
"Night Drive in Progress (SDP)": 15,
"ND Hold (SDP)": 16,
"Project Lead Review (SDP)": 17,
"Done (SDP)": 18,
"Requirement Scrub (SDP)": 19,
"Rejected (SDP)": 20
]
return statusMap[status] ?: 999 // Default high value for unknown statuses
}
// Function to transition the Epic to the desired status
def transitionEpicToStatus(Issue epic, String targetStatus) {
def transitionMap = [
"To Do (SDP)": 16,
"Design (SDP)": 27,
"Design Hold (SDP)": 29,
"Design Review (SDP)": 26,
"Working (SDP)": 5,
"Working Hold (SDP)": 20,
"Development Testing (SDP)": 18,
"DevTest Hold (SDP)": 21,
"Code Review (SDP)": 8,
"CR Hold (SDP)": 23,
"Integration Testing (SDP)": 6,
"Integration Hold (SDP)": 22,
"LWR in Progress (SDP)": 9,
"LWR Hold (SDP)": 24,
"Night Drive in Progress (SDP)": 10,
"ND Hold (SDP)": 25,
"Project Lead Review (SDP)": 11,
"Done (SDP)": 15,
"Requirement Scrub (SDP)": 28,
"Rejected (SDP)": 14
]
def transitionId = transitionMap[targetStatus]
if (transitionId) {
try {
def transitionValidationResult = issueService.validateTransition(user, epic.id, transitionId, new HashMap<>())
if (transitionValidationResult.isValid()) {
def transitionResult = issueService.transition(user, transitionValidationResult)
if (transitionResult.isValid()) {
log.info("Transitioned Epic ${epic.key} to ${targetStatus}.")
} else {
log.error("Failed to transition Epic ${epic.key} to ${targetStatus}: ${transitionResult.errorCollection}")
}
} else {
log.error("Transition validation failed for Epic ${epic.key} to ${targetStatus}: ${transitionValidationResult.errorCollection}")
}
} catch (Exception e) {
log.error("Failed to transition Epic ${epic.key} to ${targetStatus}: ${e.message}")
}
} else {
log.warn("No transition ID found for target status ${targetStatus}.")
}
}
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.workflow.IssueWorkflowManager
import com.atlassian.jira.workflow.WorkflowManager
import com.atlassian.jira.workflow.TransitionOptions
import org.apache.log4j.Logger
// Logger for debugging
def log = Logger.getLogger("com.example.TransitionScript")
// Initialize managers and components
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issueService = ComponentAccessor.getIssueService()
def workflowManager = ComponentAccessor.getWorkflowManager()
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
// Get the issue and its type
def issue = event.issue as MutableIssue
def issueType = issue.issueType.name
// Get the Epic Link custom field
def epicLinkField = customFieldManager.getCustomFieldObjectByName("Epic Link")
// Check if the issue is a Story, Task, or Sub-task and has an Epic Link
if (["Story", "Task", "Sub-task"].contains(issueType) && issue.getCustomFieldValue(epicLinkField)) {
def epicLink = issue.getCustomFieldValue(epicLinkField) as Issue
if (epicLink) {
// Get all child issues linked to the Epic
def childIssues = issueLinkManager.getOutwardLinks(epicLink.id)
.findAll { it.issueLinkType.name == "Epic-Story Link" }
.collect { it.destinationObject }
// Get the status of all child issues
def allStatuses = childIssues.collect { it.status.name }
// Determine the lowest status
def lowestStatus = allStatuses.min { status -> getStatusOrder(status) }
// Transition the Epic to the lowest status
if (lowestStatus) {
transitionEpicToStatus(epicLink, lowestStatus)
}
}
}
// Helper function to map statuses to their order
def getStatusOrder(String status) {
def statusMap = [
"To Do (SDP)": 1,
"Design (SDP)": 2,
"Design Hold (SDP)": 3,
"Design Review (SDP)": 4,
"Working (SDP)": 5,
"Working Hold (SDP)": 6,
"Development Testing (SDP)": 7,
"DevTest Hold (SDP)": 8,
"Code Review (SDP)": 9,
"CR Hold (SDP)": 10,
"Integration Testing (SDP)": 11,
"Integration Hold (SDP)": 12,
"LWR in Progress (SDP)": 13,
"LWR Hold (SDP)": 14,
"Night Drive in Progress (SDP)": 15,
"ND Hold (SDP)": 16,
"Project Lead Review (SDP)": 17,
"Done (SDP)": 18,
"Requirement Scrub (SDP)": 19,
"Rejected (SDP)": 20
]
return statusMap[status] ?: 999 // Default high value for unknown statuses
}
// Function to transition the Epic to the desired status
def transitionEpicToStatus(Issue epic, String targetStatus) {
def transitionMap = [
"To Do (SDP)": 16,
"Design (SDP)": 27,
"Design Hold (SDP)": 29,
"Design Review (SDP)": 26,
"Working (SDP)": 5,
"Working Hold (SDP)": 20,
"Development Testing (SDP)": 18,
"DevTest Hold (SDP)": 21,
"Code Review (SDP)": 8,
"CR Hold (SDP)": 23,
"Integration Testing (SDP)": 6,
"Integration Hold (SDP)": 22,
"LWR in Progress (SDP)": 9,
"LWR Hold (SDP)": 24,
"Night Drive in Progress (SDP)": 10,
"ND Hold (SDP)": 25,
"Project Lead Review (SDP)": 11,
"Done (SDP)": 15,
"Requirement Scrub (SDP)": 28,
"Rejected (SDP)": 14
]
def transitionId = transitionMap[targetStatus]
if (transitionId) {
try {
def transitionValidationResult = issueService.validateTransition(user, epic.id, transitionId, new HashMap<>())
if (transitionValidationResult.isValid()) {
def transitionResult = issueService.transition(user, transitionValidationResult)
if (transitionResult.isValid()) {
log.info("Transitioned Epic ${epic.key} to ${targetStatus}.")
} else {
log.error("Failed to transition Epic ${epic.key} to ${targetStatus}: ${transitionResult.errorCollection}")
}
} else {
log.error("Transition validation failed for Epic ${epic.key} to ${targetStatus}: ${transitionValidationResult.errorCollection}")
}
} catch (Exception e) {
log.error("Failed to transition Epic ${epic.key} to ${targetStatus}: ${e.message}")
}
} else {
log.warn("No transition ID found for target status ${targetStatus}.")
}
}