Ergaenze Statusuebersicht um Summenwerte

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jesko Anschütz 2026-03-22 19:12:00 +01:00
parent 5a109f95cb
commit 9727e53e35
3 changed files with 49 additions and 0 deletions

View file

@ -76,6 +76,7 @@ Zusätzlich zur Write-Route gibt es in dieser Stufe:
`GET /api/v1/screens/status` liefert eine kleine Uebersicht aller bisher berichtenden Screens mit ihrem jeweils letzten bekannten Datensatz. `GET /api/v1/screens/status` liefert eine kleine Uebersicht aller bisher berichtenden Screens mit ihrem jeweils letzten bekannten Datensatz.
Die Rueckgabe wird aktuell fuer Diagnosezwecke priorisiert sortiert: zuerst `offline`, dann `degraded`, dann `online`, innerhalb derselben Gruppe nach `screen_id`. Die Rueckgabe wird aktuell fuer Diagnosezwecke priorisiert sortiert: zuerst `offline`, dann `degraded`, dann `online`, innerhalb derselben Gruppe nach `screen_id`.
Zusaetzlich enthaelt die Antwort eine `summary` mit kompakten Counts fuer `total`, `online`, `degraded`, `offline` und `stale`.
Aktuell unterstuetzte Query-Parameter fuer die Uebersicht: Aktuell unterstuetzte Query-Parameter fuer die Uebersicht:
@ -84,6 +85,8 @@ Aktuell unterstuetzte Query-Parameter fuer die Uebersicht:
- `updated_since=<RFC3339>` zum Filtern nach `received_at` - `updated_since=<RFC3339>` zum Filtern nach `received_at`
- `limit=<positive integer>` zum Begrenzen der Anzahl zurueckgelieferter Screens - `limit=<positive integer>` zum Begrenzen der Anzahl zurueckgelieferter Screens
Die Query-Parameter beeinflussen die Liste in `screens`; die `summary` beschreibt weiterhin den gesamten aktuell bekannten Statusbestand.
`GET /api/v1/screens/{screenId}/status` liefert den zuletzt akzeptierten Status fuer einen einzelnen Screen zurueck. `GET /api/v1/screens/{screenId}/status` liefert den zuletzt akzeptierten Status fuer einen einzelnen Screen zurueck.
Wenn fuer den Screen noch kein Status vorliegt, liefert das Backend `404` mit dem gemeinsamen Fehlerumschlag. Wenn fuer den Screen noch kein Status vorliegt, liefert das Backend `404` mit dem gemeinsamen Fehlerumschlag.

View file

@ -144,9 +144,21 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc {
return return
} }
filtered := make([]playerStatusRecord, 0, len(records)) filtered := make([]playerStatusRecord, 0, len(records))
summary := map[string]int{
"total": 0,
"online": 0,
"degraded": 0,
"offline": 0,
"stale": 0,
}
for i := range records { for i := range records {
records[i].Stale = isStale(records[i], store.Now()) records[i].Stale = isStale(records[i], store.Now())
records[i].DerivedState = deriveState(records[i]) records[i].DerivedState = deriveState(records[i])
summary["total"]++
summary[records[i].DerivedState]++
if records[i].Stale {
summary["stale"]++
}
if updatedSince != nil && !isUpdatedSince(records[i], *updatedSince) { if updatedSince != nil && !isUpdatedSince(records[i], *updatedSince) {
continue continue
} }
@ -179,6 +191,7 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc {
} }
writeJSON(w, http.StatusOK, map[string]any{ writeJSON(w, http.StatusOK, map[string]any{
"summary": summary,
"screens": filtered, "screens": filtered,
}) })
} }

View file

@ -426,6 +426,13 @@ func TestHandleListLatestPlayerStatuses(t *testing.T) {
} }
var response struct { var response struct {
Summary struct {
Total int `json:"total"`
Online int `json:"online"`
Degraded int `json:"degraded"`
Offline int `json:"offline"`
Stale int `json:"stale"`
} `json:"summary"`
Screens []playerStatusRecord `json:"screens"` Screens []playerStatusRecord `json:"screens"`
} }
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
@ -443,6 +450,10 @@ func TestHandleListLatestPlayerStatuses(t *testing.T) {
if got, want := response.Screens[1].ScreenID, "screen-b"; got != want { if got, want := response.Screens[1].ScreenID, "screen-b"; got != want {
t.Fatalf("response.Screens[1].ScreenID = %q, want %q", got, want) t.Fatalf("response.Screens[1].ScreenID = %q, want %q", got, want)
} }
if got, want := response.Summary.Total, 2; got != want {
t.Fatalf("response.Summary.Total = %d, want %d", got, want)
}
} }
func TestHandleListLatestPlayerStatusesOrdersProblematicScreensFirst(t *testing.T) { func TestHandleListLatestPlayerStatusesOrdersProblematicScreensFirst(t *testing.T) {
@ -460,6 +471,13 @@ func TestHandleListLatestPlayerStatusesOrdersProblematicScreensFirst(t *testing.
handleListLatestPlayerStatuses(store)(w, req) handleListLatestPlayerStatuses(store)(w, req)
var response struct { var response struct {
Summary struct {
Total int `json:"total"`
Online int `json:"online"`
Degraded int `json:"degraded"`
Offline int `json:"offline"`
Stale int `json:"stale"`
} `json:"summary"`
Screens []playerStatusRecord `json:"screens"` Screens []playerStatusRecord `json:"screens"`
} }
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
@ -497,6 +515,13 @@ func TestHandleListLatestPlayerStatusesFiltersByConnectivityAndStale(t *testing.
} }
var response struct { var response struct {
Summary struct {
Total int `json:"total"`
Online int `json:"online"`
Degraded int `json:"degraded"`
Offline int `json:"offline"`
Stale int `json:"stale"`
} `json:"summary"`
Screens []playerStatusRecord `json:"screens"` Screens []playerStatusRecord `json:"screens"`
} }
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
@ -510,6 +535,14 @@ func TestHandleListLatestPlayerStatusesFiltersByConnectivityAndStale(t *testing.
if got, want := response.Screens[0].ScreenID, "screen-offline"; got != want { if got, want := response.Screens[0].ScreenID, "screen-offline"; got != want {
t.Fatalf("response.Screens[0].ScreenID = %q, want %q", got, want) t.Fatalf("response.Screens[0].ScreenID = %q, want %q", got, want)
} }
if got, want := response.Summary.Offline, 1; got != want {
t.Fatalf("response.Summary.Offline = %d, want %d", got, want)
}
if got, want := response.Summary.Online, 1; got != want {
t.Fatalf("response.Summary.Online = %d, want %d", got, want)
}
} }
func TestHandleListLatestPlayerStatusesAppliesLimit(t *testing.T) { func TestHandleListLatestPlayerStatusesAppliesLimit(t *testing.T) {