import groovy.json.JsonOutput // -------------------------- Konfiguration -------------------------- boolean DRY_RUN = true // erst prüfen; auf false stellen, wenn die Kandidatenliste stimmt int PAGE_SIZE = 50 Set EXCLUDE_BY_NAME = [ 'Default Screen Scheme' ] as Set // -------------------------- Logging ------------------------------- void logInfo(String msg){ try { logger.info(msg) } catch(e){ println msg } } void logWarn(String msg){ try { logger.warn(msg) } catch(e){ println "WARN: " + msg } } void logErr (String msg){ try { logger.error(msg)} catch(e){ println "ERR: " + msg } } // -------------------------- HTTP Helpers -------------------------- Map getAsMap(String path, Map q=[:]) { def req = get(path) q.each { k,v -> req = req.queryString(k, v) } def resp = req.asObject(Map) if (resp.status != 200) { throw new RuntimeException("GET " + path + " failed: HTTP " + resp.status + " :: " + resp.body) } return (resp.body ?: [:]) as Map } List pagedGetValues(String path, int pageSize) { int startAt = 0 List all = [] while (true) { Map body = getAsMap(path, [startAt: startAt, maxResults: pageSize]) List vals = (body.values ?: []) as List all.addAll(vals as List) int total = (body.total ?: (startAt + vals.size())) as int int nextStart = startAt + ((body.maxResults ?: vals.size()) as int) if (vals.isEmpty() || nextStart >= total) break startAt = nextStart } return all } // -------------------------- Fetchers ------------------------------ List fetchScreenSchemes(int pageSize) { logInfo("Lade Screen Schemes…") List list = pagedGetValues("/rest/api/3/screenscheme", pageSize) logInfo("Screen Schemes gefunden: " + list.size()) return list } List fetchIssueTypeScreenSchemes(int pageSize) { logInfo("Lade Issue Type Screen Schemes…") List list = pagedGetValues("/rest/api/3/issuetypescreenscheme", pageSize) logInfo("Issue Type Screen Schemes gefunden: " + list.size()) return list } /** * Holt alle Referenzen auf Screen Schemes: * - defaultScreenSchemeId je ITSS * - globale ITSS→IssueType→ScreenScheme Mappings aus /issuetypescreenscheme/mapping */ Set fetchReferencedScreenSchemeIds(int pageSize, List itssList) { Set refs = new LinkedHashSet<>() // 1) Default-Zuordnungen je ITSS for (Map itss : itssList) { def defId = itss.get("defaultScreenSchemeId") if (defId != null) refs.add(Long.valueOf(defId.toString())) } // 2) Globale Mappings logInfo("Lade globale IssueType→ScreenScheme Mappings…") List globalMaps = pagedGetValues("/rest/api/3/issuetypescreenscheme/mapping", pageSize) for (Map m : globalMaps) { def ssId = m.get("screenSchemeId") if (ssId != null) refs.add(Long.valueOf(ssId.toString())) } logInfo("Referenzierte Screen Schemes gesamt: " + refs.size()) return refs } // -------------------------- Delete ------------------------------- boolean deleteScreenScheme(long id, String name) { def resp = delete("/rest/api/3/screenscheme/" + id).asString() if (resp.status in [200,204]) { logInfo("Gelöscht: [" + id + "] " + name) return true } logWarn("Nicht gelöscht [" + id + "] " + name + " :: HTTP " + resp.status + " :: " + resp.body) return false } // -------------------------- Main ------------------------------- void runCleanup(boolean dryRun, int pageSize, Set excludeByName) { List screenSchemes = fetchScreenSchemes(pageSize) if (screenSchemes.isEmpty()) { logInfo("Keine Screen Schemes vorhanden – nichts zu tun."); return } List itssList = fetchIssueTypeScreenSchemes(pageSize) Set referenced = fetchReferencedScreenSchemeIds(pageSize, itssList) List candidates = [] for (Map s : screenSchemes) { long id = Long.valueOf(s.get("id").toString()) String name = (s.get("name") ?: "") as String if (!referenced.contains(id) && !excludeByName.contains(name)) { candidates.add([id: id, name: name, description: (s.get("description") ?: "")]) } } candidates.sort { a, b -> a.name <=> b.name } if (candidates.isEmpty()) { logInfo("Keine ungenutzten Screen Schemes gefunden. ✅"); return } logWarn("Ungenutzte Kandidaten (" + candidates.size() + "):") for (Map c : candidates) logWarn(" - [" + c.id + "] " + c.name) if (dryRun) { logInfo("Dry-Run aktiv → nichts gelöscht.") logInfo("JSON Dump:\n" + JsonOutput.prettyPrint(JsonOutput.toJson(candidates))) return } int deleted = 0, skipped = 0 for (Map c : candidates) { try { if (deleteScreenScheme((c.id as Long), c.name.toString())) deleted++ else skipped++ } catch (Exception ex) { skipped++ logErr("Fehler beim Löschen [" + c.id + "] " + c.name + " :: " + ex.message) } } logWarn("Fertig. Ergebnis: deleted=" + deleted + ", skipped=" + skipped) } // ---- Start ---- runCleanup(DRY_RUN, PAGE_SIZE, EXCLUDE_BY_NAME)