controller: expose kind/group in the services API response

The React ControllerPage partitions services on `service.group`, but
ServiceStatusResponse did not declare the new kind/group fields, so
Pydantic silently stripped them from /api/services and the React UI
rendered one flat, ungrouped table. The fallback UI was unaffected
because it is server-rendered without the response model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Roland 2026-07-23 18:44:59 -06:00
parent 36f84941db
commit 5a11e23a56
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
2 changed files with 24 additions and 0 deletions

View file

@ -348,6 +348,8 @@ class ServiceStatusResponse(BaseModel):
view_path: Optional[str] = Field(default="", description="Path to web UI")
use_https: Optional[bool] = Field(default=False, description="Whether service uses HTTPS")
description: Optional[str] = Field(default="", description="Service description")
kind: Optional[str] = Field(default=None, description="Service kind (persistent or task)")
group: Optional[str] = Field(default=None, description="Display group (core or optional)")
available: Optional[bool] = Field(default=None, description="Whether management is available")
reason: Optional[str] = Field(default=None, description="Reason if unavailable")
error: Optional[str] = Field(default=None, description="Error message if any")

View file

@ -22,6 +22,28 @@ class TestServicesListEndpoint:
# Should be a list (when docker can manage) or error dict
assert isinstance(data, (list, dict))
def test_services_list_includes_group_and_kind(self, test_client):
"""The React UI partitions on `group`; the response model must not
strip it (or `kind`) from the systemd status dicts."""
statuses = [
{"name": "wrolpi-api", "status": "running", "enabled": True,
"kind": "persistent", "group": "core"},
{"name": "wrolpi-repair", "status": "stopped", "enabled": False,
"kind": "task", "group": "optional"},
]
with mock.patch("controller.api.services.is_docker_mode", return_value=False):
with mock.patch(
"controller.api.services.get_all_services_status",
return_value=statuses,
):
response = test_client.get("/api/services")
assert response.status_code == 200
data = {s["name"]: s for s in response.json()}
assert data["wrolpi-api"]["group"] == "core"
assert data["wrolpi-api"]["kind"] == "persistent"
assert data["wrolpi-repair"]["group"] == "optional"
assert data["wrolpi-repair"]["kind"] == "task"
class TestServicesListDockerMode:
"""Tests for /api/services in Docker mode."""