Skip to content

Commit a4e12a9

Browse files
authored
refactor(api): update compliance report endpoints and enhance query parameters (#9338)
1 parent 7b1915e commit a4e12a9

File tree

3 files changed

+300
-57
lines changed

3 files changed

+300
-57
lines changed

api/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ All notable changes to the **Prowler API** are documented in this file.
99
- Exception handler for provider deletions during scans [(#9414)](https://github.com/prowler-cloud/prowler/pull/9414)
1010

1111
### Changed
12-
- Restore the compliance overview endpoint's mandatory filters [(#9330)](https://github.com/prowler-cloud/prowler/pull/9330)
12+
- Restore the compliance overview endpoint's mandatory filters [(#9338)](https://github.com/prowler-cloud/prowler/pull/9338)
1313

1414
---
1515

api/src/backend/api/tests/test_views.py

Lines changed: 130 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
from api.db_router import MainRouter
3737
from api.models import (
3838
AttackSurfaceOverview,
39+
ComplianceOverviewSummary,
40+
ComplianceRequirementOverview,
3941
Finding,
4042
Integration,
4143
Invitation,
@@ -56,6 +58,7 @@
5658
Scan,
5759
ScanSummary,
5860
StateChoices,
61+
StatusChoices,
5962
Task,
6063
TenantAPIKey,
6164
ThreatScoreSnapshot,
@@ -5820,16 +5823,44 @@ def test_invalid_provider_group_id(
58205823

58215824
@pytest.mark.django_db
58225825
class TestComplianceOverviewViewSet:
5823-
def test_compliance_overview_list_none(self, authenticated_client):
5826+
@pytest.fixture(autouse=True)
5827+
def mock_backfill_task(self):
5828+
with patch("api.v1.views.backfill_compliance_summaries_task.delay") as mock:
5829+
yield mock
5830+
5831+
def test_compliance_overview_list_none(
5832+
self,
5833+
authenticated_client,
5834+
tenants_fixture,
5835+
providers_fixture,
5836+
mock_backfill_task,
5837+
):
5838+
tenant = tenants_fixture[0]
5839+
provider = providers_fixture[0]
5840+
scan = Scan.objects.create(
5841+
name="empty-compliance-scan",
5842+
provider=provider,
5843+
trigger=Scan.TriggerChoices.MANUAL,
5844+
state=StateChoices.COMPLETED,
5845+
tenant=tenant,
5846+
)
5847+
58245848
response = authenticated_client.get(
58255849
reverse("complianceoverview-list"),
5826-
{"filter[scan_id]": "8d20ac7d-4cbc-435e-85f4-359be37af821"},
5850+
{"filter[scan_id]": str(scan.id)},
58275851
)
58285852
assert response.status_code == status.HTTP_200_OK
58295853
assert len(response.json()["data"]) == 0
5854+
mock_backfill_task.assert_called_once()
5855+
_, kwargs = mock_backfill_task.call_args
5856+
assert kwargs["scan_id"] == str(scan.id)
5857+
assert str(kwargs["tenant_id"]) == str(tenant.id)
58305858

58315859
def test_compliance_overview_list(
5832-
self, authenticated_client, compliance_requirements_overviews_fixture
5860+
self,
5861+
authenticated_client,
5862+
compliance_requirements_overviews_fixture,
5863+
mock_backfill_task,
58335864
):
58345865
# List compliance overviews with existing data
58355866
requirement_overview1 = compliance_requirements_overviews_fixture[0]
@@ -5859,6 +5890,90 @@ def test_compliance_overview_list(
58595890
assert "requirements_failed" in attributes
58605891
assert "requirements_manual" in attributes
58615892
assert "total_requirements" in attributes
5893+
mock_backfill_task.assert_called_once()
5894+
_, kwargs = mock_backfill_task.call_args
5895+
assert kwargs["scan_id"] == scan_id
5896+
5897+
def test_compliance_overview_list_uses_preaggregated_summaries(
5898+
self,
5899+
authenticated_client,
5900+
tenants_fixture,
5901+
providers_fixture,
5902+
mock_backfill_task,
5903+
):
5904+
tenant = tenants_fixture[0]
5905+
provider = providers_fixture[0]
5906+
scan = Scan.objects.create(
5907+
name="preaggregated-scan",
5908+
provider=provider,
5909+
trigger=Scan.TriggerChoices.MANUAL,
5910+
state=StateChoices.COMPLETED,
5911+
tenant=tenant,
5912+
)
5913+
5914+
ComplianceRequirementOverview.objects.create(
5915+
tenant=tenant,
5916+
scan=scan,
5917+
compliance_id="cis_1.4_aws",
5918+
framework="CIS-1.4-AWS",
5919+
version="1.4",
5920+
description="CIS AWS Foundations Benchmark v1.4.0",
5921+
region="eu-west-1",
5922+
requirement_id="framework-metadata",
5923+
requirement_status=StatusChoices.PASS,
5924+
passed_checks=1,
5925+
failed_checks=0,
5926+
total_checks=1,
5927+
)
5928+
5929+
ComplianceOverviewSummary.objects.create(
5930+
tenant=tenant,
5931+
scan=scan,
5932+
compliance_id="cis_1.4_aws",
5933+
requirements_passed=5,
5934+
requirements_failed=1,
5935+
requirements_manual=2,
5936+
total_requirements=8,
5937+
)
5938+
5939+
response = authenticated_client.get(
5940+
reverse("complianceoverview-list"),
5941+
{"filter[scan_id]": str(scan.id)},
5942+
)
5943+
5944+
assert response.status_code == status.HTTP_200_OK
5945+
data = response.json()["data"]
5946+
assert len(data) == 1
5947+
overview = data[0]
5948+
assert overview["id"] == "cis_1.4_aws"
5949+
assert overview["attributes"]["requirements_passed"] == 5
5950+
assert overview["attributes"]["requirements_failed"] == 1
5951+
assert overview["attributes"]["requirements_manual"] == 2
5952+
assert overview["attributes"]["total_requirements"] == 8
5953+
assert "framework" in overview["attributes"]
5954+
assert "version" in overview["attributes"]
5955+
mock_backfill_task.assert_not_called()
5956+
5957+
def test_compliance_overview_region_filter_skips_backfill(
5958+
self,
5959+
authenticated_client,
5960+
compliance_requirements_overviews_fixture,
5961+
mock_backfill_task,
5962+
):
5963+
requirement_overview = compliance_requirements_overviews_fixture[0]
5964+
scan_id = str(requirement_overview.scan.id)
5965+
5966+
response = authenticated_client.get(
5967+
reverse("complianceoverview-list"),
5968+
{
5969+
"filter[scan_id]": scan_id,
5970+
"filter[region]": requirement_overview.region,
5971+
},
5972+
)
5973+
5974+
assert response.status_code == status.HTTP_200_OK
5975+
assert len(response.json()["data"]) >= 1
5976+
mock_backfill_task.assert_not_called()
58625977

58635978
def test_compliance_overview_metadata(
58645979
self, authenticated_client, compliance_requirements_overviews_fixture
@@ -6012,6 +6127,11 @@ def test_compliance_overview_task_management_integration(
60126127
requirement_overview1 = compliance_requirements_overviews_fixture[0]
60136128
scan_id = str(requirement_overview1.scan.id)
60146129

6130+
# Remove existing compliance data so the view falls back to task checks
6131+
scan = requirement_overview1.scan
6132+
ComplianceOverviewSummary.objects.filter(scan=scan).delete()
6133+
ComplianceRequirementOverview.objects.filter(scan=scan).delete()
6134+
60156135
# Mock a running task
60166136
with patch.object(
60176137
ComplianceOverviewViewSet, "get_task_response_if_running"
@@ -6039,6 +6159,11 @@ def test_compliance_overview_task_failed_exception(
60396159
requirement_overview1 = compliance_requirements_overviews_fixture[0]
60406160
scan_id = str(requirement_overview1.scan.id)
60416161

6162+
# Remove existing compliance data so the view falls back to task checks
6163+
scan = requirement_overview1.scan
6164+
ComplianceOverviewSummary.objects.filter(scan=scan).delete()
6165+
ComplianceRequirementOverview.objects.filter(scan=scan).delete()
6166+
60426167
# Mock a failed task
60436168
with patch.object(
60446169
ComplianceOverviewViewSet, "get_task_response_if_running"
@@ -6062,6 +6187,8 @@ def test_compliance_overview_task_failed_exception(
60626187
("framework", "framework", 1),
60636188
("version", "version", 1),
60646189
("region", "region", 1),
6190+
("region__in", "region", 1),
6191+
("region.in", "region", 1),
60656192
],
60666193
)
60676194
def test_compliance_overview_filters(

0 commit comments

Comments
 (0)