From f985a99ea16f4b4e34682cae257631ffde402cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesko=20Ansch=C3=BCtz?= Date: Thu, 26 Mar 2026 23:05:25 +0100 Subject: [PATCH] feat(api): display_state im Player-Status-Report persistieren --- .../backend/internal/httpapi/playerstatus.go | 12 +++++++++- .../internal/httpapi/playerstatus_test.go | 24 +++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/server/backend/internal/httpapi/playerstatus.go b/server/backend/internal/httpapi/playerstatus.go index 2428d2c..69dff40 100644 --- a/server/backend/internal/httpapi/playerstatus.go +++ b/server/backend/internal/httpapi/playerstatus.go @@ -2,12 +2,15 @@ package httpapi import ( "errors" + "log/slog" "net/http" "net/url" "sort" "strconv" "strings" "time" + + storePackage "git.az-it.net/az/morz-infoboard/server/backend/internal/store" ) type screenStatusSummary struct { @@ -47,6 +50,7 @@ type playerStatusRequest struct { HeartbeatEverySeconds int `json:"heartbeat_every_seconds"` StartedAt string `json:"started_at"` LastHeartbeatAt string `json:"last_heartbeat_at"` + DisplayState string `json:"display_state,omitempty"` } // playerStatusMQTTConfig is the MQTT configuration returned to agents in the @@ -63,7 +67,7 @@ type playerStatusResponse struct { MQTT *playerStatusMQTTConfig `json:"mqtt,omitempty"` } -func handlePlayerStatus(store playerStatusStore, mqttBroker, mqttUsername, mqttPassword string) http.HandlerFunc { +func handlePlayerStatus(store playerStatusStore, screenStore *storePackage.ScreenStore, mqttBroker, mqttUsername, mqttPassword string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var request playerStatusRequest if err := decodeJSON(r, &request); err != nil { @@ -130,6 +134,12 @@ func handlePlayerStatus(store playerStatusStore, mqttBroker, mqttUsername, mqttP LastHeartbeatAt: request.LastHeartbeatAt, }) + if request.DisplayState != "" && screenStore != nil { + if err := screenStore.UpsertDisplayState(r.Context(), request.ScreenID, request.DisplayState); err != nil { + slog.Error("upsert display state", "screen_id", request.ScreenID, "err", err) + } + } + resp := playerStatusResponse{Status: "accepted"} if mqttBroker != "" { resp.MQTT = &playerStatusMQTTConfig{ diff --git a/server/backend/internal/httpapi/playerstatus_test.go b/server/backend/internal/httpapi/playerstatus_test.go index 0c4206f..5859e5c 100644 --- a/server/backend/internal/httpapi/playerstatus_test.go +++ b/server/backend/internal/httpapi/playerstatus_test.go @@ -26,7 +26,7 @@ func TestHandlePlayerStatusAccepted(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(store, "", "", "")(w, req) + handlePlayerStatus(store, nil, "", "", "")(w, req) if got, want := w.Code, http.StatusOK; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -66,7 +66,7 @@ func TestHandlePlayerStatusRejectsInvalidJSON(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewBufferString("{")) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -83,7 +83,7 @@ func TestHandlePlayerStatusRejectsMissingScreenID(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -102,7 +102,7 @@ func TestHandlePlayerStatusStoresNormalizedScreenID(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(store, "", "", "")(w, req) + handlePlayerStatus(store, nil, "", "", "")(w, req) if got, want := w.Code, http.StatusOK; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -122,7 +122,7 @@ func TestHandlePlayerStatusRejectsMissingTimestamp(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -138,7 +138,7 @@ func TestHandlePlayerStatusRejectsMissingStatus(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -155,7 +155,7 @@ func TestHandlePlayerStatusRejectsUnknownStatus(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -173,7 +173,7 @@ func TestHandlePlayerStatusRejectsUnknownServerConnectivity(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -191,7 +191,7 @@ func TestHandlePlayerStatusRejectsNonPositiveHeartbeatInterval(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -208,7 +208,7 @@ func TestHandlePlayerStatusRejectsMalformedTimestamps(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -226,7 +226,7 @@ func TestHandlePlayerStatusRejectsMalformedStartedAt(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want) @@ -244,7 +244,7 @@ func TestHandlePlayerStatusRejectsMalformedLastHeartbeatAt(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/v1/player/status", bytes.NewReader(body)) w := httptest.NewRecorder() - handlePlayerStatus(newInMemoryPlayerStatusStore(), "", "", "")(w, req) + handlePlayerStatus(newInMemoryPlayerStatusStore(), nil, "", "", "")(w, req) if got, want := w.Code, http.StatusBadRequest; got != want { t.Fatalf("status = %d, want %d", got, want)