Feature #47
closedFeature #24: Implémenter un hook pour la création automatique de tickets Redmine depuis les scans Trivy et Semgrep
Infrastructure Redmine : recherche par clé (champ custom), création + anti-doublons/régression
0%
conversation:21#24
Description
Couche infrastructure de `api/` : adaptateur Redmine concret derrière le port défini par l'application, réutilisant `RedmineClient`, les value objects `RedmineStatus`/`RedmineField` et `create_ticket_from_draft`. Recherche d'un ticket existant par clé via un champ custom `security_key` (repli par recherche titre/clé documenté). État : ticket **ouvert** portant la clé → ne pas recréer (anti-doublon) ; seul un ticket **résolu/fermé** → recréer (régression). Création depuis le draft formaté : tag `Sécurité`, statut `Submitted`, `security_key` renseigné. TDD avec respx ; intégration optionnelle `RUN_REDMINE_LIVE`.
## Critères d'acceptation
- Recherche par clé via champ custom `security_key` (repli titre/clé documenté).
- Anti-doublon : aucun ticket si un ticket ouvert porte déjà la clé.
- Régression : nouveau ticket si seuls des tickets résolus/fermés portent la clé.
- Création conforme : tag `Sécurité`, statut `Submitted`, clé persistée.
- Tests respx verts (format + scénarios dédup/régression) ; chemin `RUN_REDMINE_LIVE` présent ; ruff + mypy passent.
Estimation : M. Dépend de : « Domaine & application … ».
Files
RA Updated by Redmine Admin about 2 months ago
- Status changed from Submitted to Spec
- spec_ref updated (diff)
RA Updated by Redmine Admin about 2 months ago
- Status changed from Spec to In development
Prise en charge backend : passage en *In development*. Implémentation de l'adaptateur Redmine d'infrastructure (port défini en #46), TDD avec respx.
RA Updated by Redmine Admin about 2 months ago
- Status changed from In development to QA
- branch set to feat/47-redmine-security-adapter
- pr_url set to https://github.com/omdev-tech/PipeLiner-Client/pull/27
Adaptateur Redmine d'infrastructure livré, PR ouverte vers `dev` : https://github.com/omdev-tech/PipeLiner-Client/pull/27
Résumé :
- `RedmineScanTicketAdapter` derrière le port `ScanTicketPort` (#46), réutilise `RedmineClient` + `RedmineStatus`/`RedmineField`.
- Recherche par clé : champ custom `security_key` (id 12) si configuré, sinon repli titre/description documenté. Ouvert → anti-doublon ; uniquement résolu/fermé → régression.
- Création conforme : tag `[Sécurité]`, statut `Submitted`, clé persistée.
- Tests respx verts + chemin `RUN_REDMINE_LIVE` ; gate vert : pytest ×2 (423 passed, 2 skipped) sur pgvector éphémère, ruff, mypy. Doc ajoutée.
Branche : `feat/47-redmine-security-adapter`. Passage en QA.
RA Updated by Redmine Admin about 2 months ago
RA Updated by Redmine Admin about 2 months ago
**QA — VERDICT : PASS** ✅ (le ticket reste en QA, prêt pour promotion preprod — hors périmètre QA)
Smoke test backend-only sur la branche `feat/47-redmine-security-adapter` (SHA `bf0d87d`, PR #27 → dev). Pas d'UI à exercer → les suites de tests servent de preuve (fallback prévu). Preuve technique filtrée des secrets jointe : **qa-47-preuve-technique.txt**.
**Commandes exécutées (api/.venv, PYTHONPATH=src)**
- `pytest -q tests/test_redmine_scan_adapter.py` → **9 passed**
- + `tests/test_redmine_scan_adapter_live.py` → **9 passed, 1 skipped** (chemin `RUN_REDMINE_LIVE` gated, non exécuté)
- Câblage import : `RedmineScanTicketAdapter` est bien un `ScanTicketPort` (port #46) ; `build_redmine_scan_ticket_adapter` + `SECURITY_TAG` importables ; `RedmineField.SECURITY_KEY = 12`
- `ruff check` (scan_adapter + test) → All checks passed
- `mypy scan_adapter.py` → Success, no issues
**Critères d'acceptation → résultat**
| AC | Résultat | Preuve |
|----|----------|--------|
| AC1 — Recherche par clé via champ custom `security_key` (repli titre/description documenté) | **OK** | `test_find_by_key_open_ticket_via_custom_field`, `test_find_by_key_fallback_matches_on_description` ; repli documenté dans la docstring de `scan_adapter.py` |
| AC2 — Anti-doublon : aucun ticket si un ticket **ouvert** porte la clé (`resolved=False`) | **OK** | `test_find_by_key_open_ticket_via_custom_field` (pass 1 scopée `status_id=open`) |
| AC3 — Régression : nouveau ticket si seuls des tickets **fermés** portent la clé (`resolved=True`) | **OK** | `test_find_by_key_only_closed_ticket_is_regression` |
| AC4 — Création conforme : tag `[Sécurité]`, statut `Submitted`, clé persistée | **OK** | `test_create_ticket_files_security_ticket`, `test_create_ticket_without_field_embeds_key_in_body` |
| AC5 — respx verts + chemin `RUN_REDMINE_LIVE` présent + ruff/mypy | **OK** | 9/9 respx verts, 1 live skipped, ruff+mypy clean ; gate dev 423 passed / 2 skipped ×2 |
**Note AC1 (provisioning)** : le champ custom `security_key` (id 12) n'est pas encore provisionné sur le Redmine live ; l'adaptateur retombe alors proprement sur le **repli titre/description** (clé embarquée dans le corps à la création). Comportement conforme et couvert par tests — AC validé.