Skip to content

Commit cf6557a

Browse files
authored
fix(api): handle organizations with no emissions in /sums (#693) (#1172)
GET /organizations/{id}/sums returns 500 with `{"detail": "Generic error"}` for any organization that has no emissions logged in the requested period. From the dashboard side this makes new or quiet organizations look broken. Tracing through `get_organization_detailed_sums`, the joins are outer joins but the timestamp filter on `Emission` ends up in the WHERE clause and so cancels them out for empty organizations. The query returns nothing, `.first()` returns `None`, and the router hands `None` back to FastAPI which trips the global exception handler and masks the real shape of the response. I kept the SQL alone and handled the empty case in the usecase, where `get_one_organization` is already available for the metadata. When the sums query comes back as `None` we look the organization up and return a zero-valued `OrganizationReport`. Rebuilding the joins would be a larger, more invasive change than what the issue asks for. Added a parallel unit test in `tests/api/usecase/organization/test_organization_detailed_sums.py` that mocks both repository methods so it runs without a database. On the unfixed code it fails on the first attribute access on `None`; on the fixed code it passes. The existing happy-path test is unchanged. Closes #693
1 parent edd49c8 commit cf6557a

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

carbonserver/carbonserver/api/usecases/organization/organization_sum.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,27 @@ def compute_detailed_sum(
1616
start_date,
1717
end_date,
1818
)
19-
return sums
19+
if sums is not None:
20+
return sums
21+
22+
# No emissions in the requested period. Return zeros so the dashboard
23+
# still gets a valid report rather than a 500.
24+
organization = self._organization_repository.get_one_organization(
25+
organization_id
26+
)
27+
return OrganizationReport(
28+
organization_id=organization.id,
29+
name=organization.name,
30+
description=organization.description,
31+
emissions=0.0,
32+
cpu_power=0.0,
33+
gpu_power=0.0,
34+
ram_power=0.0,
35+
cpu_energy=0.0,
36+
gpu_energy=0.0,
37+
ram_energy=0.0,
38+
energy_consumed=0.0,
39+
duration=0,
40+
emissions_rate=0.0,
41+
emissions_count=0,
42+
)

carbonserver/tests/api/usecase/organization/test_organization_detailed_sums.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from datetime import datetime
22
from unittest import mock
3+
from uuid import UUID
34

45
import dateutil.relativedelta
56

67
from carbonserver.api.infra.repositories.repository_organizations import (
78
SqlAlchemyRepository,
89
)
10+
from carbonserver.api.schemas import Organization
911
from carbonserver.api.usecases.organization.organization_sum import (
1012
OrganizationSumsUsecase,
1113
)
@@ -53,3 +55,27 @@ def test_sum_computes_for_organization_id():
5355
actual_organization_global_sum_by_experiment[0]["emissions"]
5456
== expected_emission_sum
5557
)
58+
59+
60+
def test_sum_returns_zero_report_when_organization_has_no_emissions():
61+
"""Issue #693: an organization with no emissions in the requested period
62+
should get back a zero-valued report instead of triggering the global
63+
"Generic error" handler.
64+
"""
65+
repository_mock = mock.Mock(spec=SqlAlchemyRepository)
66+
repository_mock.get_organization_detailed_sums.return_value = None
67+
repository_mock.get_one_organization.return_value = Organization(
68+
id=UUID(ORG_ID),
69+
name="Quiet Org",
70+
description="An organization that has not logged anything yet.",
71+
)
72+
usecase = OrganizationSumsUsecase(repository_mock)
73+
74+
report = usecase.compute_detailed_sum(ORG_ID, START_DATE, END_DATE)
75+
76+
assert report.organization_id == UUID(ORG_ID)
77+
assert report.name == "Quiet Org"
78+
assert report.emissions == 0.0
79+
assert report.energy_consumed == 0.0
80+
assert report.duration == 0
81+
assert report.emissions_count == 0

0 commit comments

Comments
 (0)