Priorisiere Statusuebersicht fuer Diagnosefaelle

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 18:52:18 +01:00
parent 4ba3b4ddef
commit cbe0c40f45
3 changed files with 61 additions and 0 deletions

View file

@ -75,6 +75,7 @@ Zusätzlich zur Write-Route gibt es in dieser Stufe:
- `GET /api/v1/screens/{screenId}/status` - `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. `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. `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

@ -2,6 +2,7 @@ package httpapi
import ( import (
"net/http" "net/http"
"sort"
"strings" "strings"
"time" "time"
) )
@ -149,6 +150,16 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc {
filtered = append(filtered, records[i]) 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{ writeJSON(w, http.StatusOK, map[string]any{
"screens": filtered, "screens": filtered,
}) })
@ -197,3 +208,14 @@ func deriveState(record playerStatusRecord) string {
return "online" return "online"
} }
func derivedStatePriority(state string) int {
switch state {
case "offline":
return 0
case "degraded":
return 1
default:
return 2
}
}

View file

@ -439,6 +439,44 @@ func TestHandleListLatestPlayerStatuses(t *testing.T) {
if got, want := response.Screens[0].ScreenID, "screen-a"; got != want { if got, want := response.Screens[0].ScreenID, "screen-a"; 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.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) { func TestHandleListLatestPlayerStatusesFiltersByConnectivityAndStale(t *testing.T) {