/** * ----------------------------------------------------------------------------- * 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}") }