diff --git a/docs/PLAYER-STATUS-HTTP.md b/docs/PLAYER-STATUS-HTTP.md index b4f59f6..d2fad87 100644 --- a/docs/PLAYER-STATUS-HTTP.md +++ b/docs/PLAYER-STATUS-HTTP.md @@ -75,6 +75,7 @@ Zusätzlich zur Write-Route gibt es in dieser Stufe: - `GET /api/v1/screens/{screenId}/status` `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`. `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. diff --git a/server/backend/internal/httpapi/playerstatus.go b/server/backend/internal/httpapi/playerstatus.go index 0727021..671140e 100644 --- a/server/backend/internal/httpapi/playerstatus.go +++ b/server/backend/internal/httpapi/playerstatus.go @@ -2,6 +2,7 @@ package httpapi import ( "net/http" + "sort" "strings" "time" ) @@ -149,6 +150,16 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc { filtered = append(filtered, records[i]) } + sort.Slice(filtered, func(i, j int) bool { + leftPriority := derivedStatePriority(filtered[i].DerivedState) + rightPriority := derivedStatePriority(filtered[j].DerivedState) + if leftPriority != rightPriority { + return leftPriority < rightPriority + } + + return filtered[i].ScreenID < filtered[j].ScreenID + }) + writeJSON(w, http.StatusOK, map[string]any{ "screens": filtered, }) @@ -197,3 +208,14 @@ func deriveState(record playerStatusRecord) string { return "online" } + +func derivedStatePriority(state string) int { + switch state { + case "offline": + return 0 + case "degraded": + return 1 + default: + return 2 + } +} diff --git a/server/backend/internal/httpapi/playerstatus_test.go b/server/backend/internal/httpapi/playerstatus_test.go index 1669e6d..9e4efc6 100644 --- a/server/backend/internal/httpapi/playerstatus_test.go +++ b/server/backend/internal/httpapi/playerstatus_test.go @@ -439,6 +439,44 @@ func TestHandleListLatestPlayerStatuses(t *testing.T) { if got, want := response.Screens[0].ScreenID, "screen-a"; got != want { t.Fatalf("response.Screens[0].ScreenID = %q, want %q", got, want) } + + if got, want := response.Screens[1].ScreenID, "screen-b"; got != want { + t.Fatalf("response.Screens[1].ScreenID = %q, want %q", got, want) + } +} + +func TestHandleListLatestPlayerStatusesOrdersProblematicScreensFirst(t *testing.T) { + store := newInMemoryPlayerStatusStore() + store.now = func() time.Time { + return time.Date(2026, 3, 22, 16, 10, 0, 0, time.UTC) + } + store.Save(playerStatusRecord{ScreenID: "screen-online", Timestamp: "2026-03-22T16:09:30Z", Status: "running", ServerConnectivity: "online", ReceivedAt: "2026-03-22T16:09:30Z", HeartbeatEverySeconds: 30}) + store.Save(playerStatusRecord{ScreenID: "screen-offline", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "offline", ReceivedAt: "2026-03-22T16:00:00Z", HeartbeatEverySeconds: 30}) + store.Save(playerStatusRecord{ScreenID: "screen-degraded", Timestamp: "2026-03-22T16:09:00Z", Status: "running", ServerConnectivity: "degraded", ReceivedAt: "2026-03-22T16:09:00Z", HeartbeatEverySeconds: 30}) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/status", nil) + w := httptest.NewRecorder() + + handleListLatestPlayerStatuses(store)(w, req) + + var response struct { + Screens []playerStatusRecord `json:"screens"` + } + if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { + t.Fatalf("Unmarshal() error = %v", err) + } + + if got, want := response.Screens[0].ScreenID, "screen-offline"; got != want { + t.Fatalf("response.Screens[0].ScreenID = %q, want %q", got, want) + } + + if got, want := response.Screens[1].ScreenID, "screen-degraded"; got != want { + t.Fatalf("response.Screens[1].ScreenID = %q, want %q", got, want) + } + + if got, want := response.Screens[2].ScreenID, "screen-online"; got != want { + t.Fatalf("response.Screens[2].ScreenID = %q, want %q", got, want) + } } func TestHandleListLatestPlayerStatusesFiltersByConnectivityAndStale(t *testing.T) {