refactor(backend/api): extract EvidenceService (Step 4 — file 9 of 18)

compliance/api/evidence_routes.py (641 LOC) -> 240 LOC thin routes + 460-line
EvidenceService. Manages evidence CRUD, file upload, CI/CD evidence
collection (SAST/dependency/SBOM/container scans), and CI status dashboard.

Service injection pattern: EvidenceService takes the EvidenceRepository,
ControlRepository, and AutoRiskUpdater classes as constructor parameters.
The route's get_evidence_service factory reads these class references from
its own module namespace so tests that
``patch("compliance.api.evidence_routes.EvidenceRepository", ...)`` still
take effect through the factory.

The `_store_evidence` and `_update_risks` helpers stay as module-level
callables in evidence_service and are re-exported from the route module.
The collect_ci_evidence handler remains inline (not delegated to a service
method) so tests can patch
`compliance.api.evidence_routes._store_evidence` and have the patch take
effect at the handler's call site.

Legacy re-exports via __all__: SOURCE_CONTROL_MAP, EvidenceRepository,
ControlRepository, AutoRiskUpdater, _parse_ci_evidence,
_extract_findings_detail, _store_evidence, _update_risks.

Verified:
  - 208/208 pytest (core + 35 evidence tests) pass
  - OpenAPI 360/484 unchanged
  - mypy compliance/ -> Success on 135 source files
  - evidence_routes.py 641 -> 240 LOC
  - Hard-cap violations: 10 -> 9

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-08 21:59:03 +02:00
parent e613af1a7d
commit a638d0e527
4 changed files with 641 additions and 538 deletions

View File

@@ -29108,7 +29108,7 @@
},
"/api/compliance/evidence/ci-status": {
"get": {
"description": "Get CI/CD evidence collection status.\n\nReturns overview of recent evidence collected from CI/CD pipelines,\nuseful for dashboards and monitoring.",
"description": "Get CI/CD evidence collection status overview.",
"operationId": "get_ci_evidence_status_api_compliance_evidence_ci_status_get",
"parameters": [
{
@@ -29117,9 +29117,16 @@
"name": "control_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Filter by control ID",
"title": "Control Id",
"type": "string"
"title": "Control Id"
}
},
{
@@ -29139,7 +29146,11 @@
"200": {
"content": {
"application/json": {
"schema": {}
"schema": {
"additionalProperties": true,
"title": "Response Get Ci Evidence Status Api Compliance Evidence Ci Status Get",
"type": "object"
}
}
},
"description": "Successful Response"
@@ -29164,7 +29175,7 @@
},
"/api/compliance/evidence/collect": {
"post": {
"description": "Collect evidence from CI/CD pipeline.\n\nThis endpoint is designed to be called from CI/CD workflows (GitHub Actions,\nGitLab CI, Jenkins, etc.) to automatically collect compliance evidence.\n\nSupported sources:\n- sast: Static Application Security Testing (Semgrep, SonarQube, etc.)\n- dependency_scan: Dependency vulnerability scanning (Trivy, Grype, Snyk)\n- sbom: Software Bill of Materials (CycloneDX, SPDX)\n- container_scan: Container image scanning (Trivy, Grype)\n- test_results: Test coverage and results\n- secret_scan: Secret detection (Gitleaks, TruffleHog)\n- code_review: Code review metrics",
"description": "Collect evidence from CI/CD pipeline.\n\nHandler stays inline so tests can patch\n``compliance.api.evidence_routes._store_evidence`` /\n``compliance.api.evidence_routes._update_risks`` directly.",
"operationId": "collect_ci_evidence_api_compliance_evidence_collect_post",
"parameters": [
{
@@ -29184,9 +29195,16 @@
"name": "ci_job_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "CI/CD Job ID for traceability",
"title": "Ci Job Id",
"type": "string"
"title": "Ci Job Id"
}
},
{
@@ -29195,9 +29213,16 @@
"name": "ci_job_url",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL to CI/CD job",
"title": "Ci Job Url",
"type": "string"
"title": "Ci Job Url"
}
}
],
@@ -29205,9 +29230,16 @@
"content": {
"application/json": {
"schema": {
"additionalProperties": true,
"title": "Report Data",
"type": "object"
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Report Data"
}
}
}
@@ -29216,7 +29248,11 @@
"200": {
"content": {
"application/json": {
"schema": {}
"schema": {
"additionalProperties": true,
"title": "Response Collect Ci Evidence Api Compliance Evidence Collect Post",
"type": "object"
}
}
},
"description": "Successful Response"
@@ -29302,7 +29338,9 @@
"200": {
"content": {
"application/json": {
"schema": {}
"schema": {
"$ref": "#/components/schemas/EvidenceResponse"
}
}
},
"description": "Successful Response"
@@ -29344,7 +29382,11 @@
"200": {
"content": {
"application/json": {
"schema": {}
"schema": {
"additionalProperties": true,
"title": "Response Delete Evidence Api Compliance Evidence Evidence Id Delete",
"type": "object"
}
}
},
"description": "Successful Response"