123 lines
4.7 KiB
Groovy
123 lines
4.7 KiB
Groovy
/**
|
||
* -----------------------------------------------------------------------------
|
||
* Workflow Postfunction (ScriptRunner for Jira Cloud)
|
||
* -----------------------------------------------------------------------------
|
||
*
|
||
* Name
|
||
* ----------------
|
||
* [CoE] Transition linked CSD ticket on close
|
||
*
|
||
* Zweck
|
||
* -----
|
||
* Beim Ausführen der Transition im CoE-Ticket soll ein eindeutig verlinktes
|
||
* Ticket im Zielprojekt (z.B. CSD-xxxx) automatisch per Transition
|
||
* weitergeschaltet werden (z.B. Status "Back from CoE").
|
||
*
|
||
* Prozess-Annahme
|
||
* ---------------
|
||
* - Es existiert genau EIN Link vom CoE-Ticket zu einem Ticket im Zielprojekt.
|
||
* - Der Linktyp (Name) ist bekannt, z.B. "is cloned by".
|
||
*
|
||
* Technischer Ansatz
|
||
* ------------------
|
||
* - HTTP-Calls (get/post) bleiben im Workflow-Kontext, weil ScriptRunner Cloud
|
||
* diese Helper dort zuverlässig bereitstellt.
|
||
* - Die Ermittlung des Ziel-Tickets ist in eine Script-Manager-Utility ausgelagert:
|
||
* utils.LinkedIssueTransitions.findSingleLinkedTargetKey(...)
|
||
*
|
||
* Konfiguration
|
||
* -------------
|
||
* - LINK_TYPE_NAME: Name der Link-Richtung (inward oder outward), wie er in Jira
|
||
* angezeigt wird (z.B. "is cloned by").
|
||
* - TRANSITION_ID: Die ID der Transition, die im Ziel-Ticket ausgeführt werden soll.
|
||
* - TARGET_PROJECT_KEY: Projekt-Key des Zielprojekts (z.B. "CSD").
|
||
*
|
||
* Logging
|
||
* -------
|
||
* Das Skript loggt:
|
||
* - Start und Konfiguration
|
||
* - Fehlerzustände (kein Source-Key, HTTP Fehler, kein eindeutiges Target)
|
||
* - Erfolg/Misserfolg der Transition im Ziel-Ticket
|
||
*
|
||
* -----------------------------------------------------------------------------
|
||
*/
|
||
|
||
import utils.LinkedIssueTransitions
|
||
|
||
// ------------------------- Konfiguration ------------------------------------
|
||
// Linktyp-Name (Richtung) – z.B. "is cloned by"
|
||
final String LINK_TYPE_NAME = "is cloned by"
|
||
|
||
// Transition im Zielprojekt – z.B. "CoE erledigt" (ID = 441)
|
||
final String TRANSITION_ID = "441"
|
||
|
||
// Zielprojekt, in dem das verlinkte Ticket liegt
|
||
final String TARGET_PROJECT_KEY = "CSD"
|
||
|
||
// Einheitlicher Log-Prefix (macht das Filtern in Logs leichter)
|
||
final String LOG_PREFIX = "[CoE->Linked Transition]"
|
||
|
||
// ------------------------- Guard: Source Issue Key --------------------------
|
||
// issue kommt aus dem Workflow-Kontext der Postfunction.
|
||
def sourceKey = issue?.key?.toString()
|
||
if (!sourceKey) {
|
||
logger.warn("${LOG_PREFIX} Kein issue.key im Kontext. Abbruch.")
|
||
return
|
||
}
|
||
|
||
logger.info("${LOG_PREFIX} Start. Source=${sourceKey}, linkType='${LINK_TYPE_NAME}', transitionId=${TRANSITION_ID}, targetProject=${TARGET_PROJECT_KEY}")
|
||
|
||
// ------------------------- 1) Source Issue laden ----------------------------
|
||
// Wir brauchen die Issue-Links (issuelinks), weil dort die verknüpften Tickets stehen.
|
||
// Hinweis: Wir laden nur das Feld "issuelinks", um Payload klein und schnell zu halten.
|
||
def issueResp = get("/rest/api/3/issue/${sourceKey}")
|
||
.queryString("fields", "issuelinks")
|
||
.asObject(Map)
|
||
|
||
// Jira REST: 200 = OK
|
||
if (issueResp.status != 200) {
|
||
logger.warn("${LOG_PREFIX} Konnte ${sourceKey} nicht laden (${issueResp.status}). Body=${issueResp.body}")
|
||
return
|
||
}
|
||
|
||
// ------------------------- 2) Zielkey finden (Utility) ----------------------
|
||
// In der Utility stecken unsere Regeln:
|
||
// - Filter nach Linktyp-Name (inward/outward)
|
||
// - Filter nach Zielprojekt-Key-Prefix (z.B. "CSD-")
|
||
// - Es muss GENAU ein Treffer sein, sonst null.
|
||
def targetKey = LinkedIssueTransitions.findSingleLinkedTargetKey(
|
||
issueResp.body,
|
||
LINK_TYPE_NAME,
|
||
TARGET_PROJECT_KEY
|
||
)
|
||
|
||
if (!targetKey) {
|
||
logger.warn("${LOG_PREFIX} Kein eindeutiges Ziel-Ticket gefunden (erwartet genau 1 Link ins Projekt ${TARGET_PROJECT_KEY}). Abbruch.")
|
||
return
|
||
}
|
||
|
||
logger.info("${LOG_PREFIX} Ziel-Ticket: ${targetKey}. Führe Transition aus…")
|
||
|
||
// ------------------------- 3) Transition im Ziel-Ticket ausführen -----------
|
||
// Jira REST Transition Endpoint:
|
||
// POST /rest/api/3/issue/{issueIdOrKey}/transitions
|
||
//
|
||
// Body:
|
||
// { "transition": { "id": "441" } }
|
||
//
|
||
// Erfolg: typischerweise 204 (No Content)
|
||
def transResp = post("/rest/api/3/issue/${targetKey}/transitions")
|
||
.header("Content-Type", "application/json")
|
||
.body([ transition: [ id: TRANSITION_ID ] ])
|
||
.asObject(Map)
|
||
|
||
if (transResp.status == 204) {
|
||
logger.info("${LOG_PREFIX} OK: ${targetKey} erfolgreich transitioniert (ID=${TRANSITION_ID}).")
|
||
} else {
|
||
// Häufige Fehlerursachen:
|
||
// - Run-as User / Add-on User hat keine Berechtigung im Zielprojekt
|
||
// - Transition-ID passt nicht zum Workflow/Status des Ziel-Tickets
|
||
// - Ziel-Ticket ist in einem Status, in dem die Transition nicht verfügbar ist
|
||
logger.warn("${LOG_PREFIX} Transition fehlgeschlagen für ${targetKey}: status=${transResp.status}, body=${transResp.body}")
|
||
}
|