Jira-Scripte/Scheduled Jobs/Schließe Tickets aus dem Status Resolved.groovy
2025-12-14 19:17:45 +01:00

120 lines
4.6 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ScriptRunner Cloud - Scheduled Job
// 1) Issues per JQL suchen (/rest/api/3/search/jql + nextPageToken)
// 2) Transition nach "Closed" (per ID ODER Ziel-Status ODER Aktionsname)
// 3) DueDate leeren
// 4) Kommentar (ADF) hinzufügen
// ===================== Konfiguration =====================
def JQL = 'project = "Customer Service Desk" AND status = Resolved AND duedate IS NOT EMPTY AND due <= endOfDay()'
def MAX_PER_PAGE = 50
// Wenn du die Transition-ID kennst, HIER setzen (als String). Beispiel: "331" für "Schließen".
// Wenn null/leer, wird via Name gesucht (erst Ziel-Status, dann Aktionsname).
final String TRANSITION_ID = "331" // <- bei dir vorhanden; sonst auf null setzen
final String TARGET_STATUS_NAME = "Closed" // Ziel-Statusname
final List<String> ACTION_NAME_FALLBACKS = ["Schließen", "Close", "Closed"] // Aktionsnamen-Fallbacks
// Kommentartext (als ADF gesendet)
def commentText = """
Dieses Ticket wurde automatisch geschlossen, nachdem der Support die Lösung präsentiert hat und wir davon ausgehen, dass die Lösung korrekt ist.
Sollten weiterhin Fragen bestehen oder erneut Unterstützung benötigt werden, können Sie eine neue Anfrage stellen.
Ihr pds Support
""".trim()
// ========================================================
def adfBody = [
type: "doc",
version: 1,
content: [[ type: "paragraph", content: [[ type: "text", text: commentText ]] ]]
]
// --- Helper: Transition-ID bestimmen ---
def resolveTransitionId = { String issueKey ->
if (TRANSITION_ID?.trim()) {
return TRANSITION_ID.trim()
}
def resp = get("/rest/api/3/issue/${issueKey}/transitions").asObject(Map)
assert resp.status == 200 : "Transitions nicht lesbar für ${issueKey}: ${resp.status} - ${resp.body}"
def transitions = (resp.body?.transitions as List) ?: []
// 1) nach Ziel-Status (to.name)
def byTargetStatus = transitions.find { it?.to?.name?.equalsIgnoreCase(TARGET_STATUS_NAME) }
if (byTargetStatus?.id) return byTargetStatus.id as String
// 2) nach Aktionsname (name)
def byActionName = transitions.find { t ->
ACTION_NAME_FALLBACKS.any { fn -> t?.name?.equalsIgnoreCase(fn) }
}
return byActionName?.id as String
}
// --- Helper: Transition ausführen ---
def doTransition = { String issueKey, String transitionId ->
def resp = post("/rest/api/3/issue/${issueKey}/transitions")
.header("Content-Type", "application/json")
.body([ transition: [ id: transitionId ] ])
.asString()
assert resp.status == 204 : "Transition fehlgeschlagen für ${issueKey}: ${resp.status} - ${resp.body}"
}
// --- Helper: DueDate leeren ---
def clearDueDate = { String issueKey ->
def resp = put("/rest/api/3/issue/${issueKey}")
.header("Content-Type", "application/json")
.body([ fields: [ duedate: null ] ])
.asString()
assert resp.status == 204 : "DueDate leeren fehlgeschlagen für ${issueKey}: ${resp.status} - ${resp.body}"
}
// --- Helper: Kommentar hinzufügen (ADF) ---
def addComment = { String issueKey, Map adf ->
def resp = post("/rest/api/3/issue/${issueKey}/comment")
.header("Content-Type", "application/json")
.body([ body: adf ])
.asObject(Map)
assert resp.status == 201 : "Kommentar fehlgeschlagen für ${issueKey}: ${resp.status} - ${resp.body}"
}
// --- Suche mit neuer API und Token-Pagination ---
def processed = 0
String nextPageToken = null
boolean isLast = false
while (!isLast) {
def req = get("/rest/api/3/search/jql")
.queryString("jql", JQL)
.queryString("fields", "status,duedate") // key ist immer dabei
.queryString("maxResults", MAX_PER_PAGE as String)
if (nextPageToken) req = req.queryString("nextPageToken", nextPageToken)
def searchResp = req.asObject(Map)
assert searchResp.status == 200 : "Search-Fehler: ${searchResp.status} - ${searchResp.body}"
def body = searchResp.body as Map
def issues = (body?.issues as List) ?: []
isLast = (body?.isLast == true)
nextPageToken = body?.nextPageToken as String
issues.each { Map iss ->
def key = iss["key"] as String
try {
def transitionId = resolveTransitionId(key)
if (!transitionId) {
logger.warn("Keine passende Transition für ${key} gefunden (Status='${TARGET_STATUS_NAME}', Fallbacks=${ACTION_NAME_FALLBACKS}) übersprungen.")
return
}
doTransition(key, transitionId)
clearDueDate(key)
addComment(key, adfBody)
processed++
logger.info("OK: ${key} -> Transition=${transitionId}, DueDate geleert, Kommentar gesetzt.")
} catch (Throwable t) {
logger.error("Fehler bei ${key}: ${t.message}")
}
}
}
logger.info("Fertig. Verarbeitete Tickets: ${processed} (JQL: ${JQL})")