Ergaenze Screen-ID-Filter (q=) fuer Uebersicht und Status-API
GET /api/v1/screens/status und GET /status akzeptieren jetzt q=<substring> zum Filtern der Ergebnisliste nach ScreenID. Der Vergleich ist case- insensitiv. Leerer Wert bedeutet kein Filter; jeder andere String ist gueltig (keine Validierung noetig). Die Summary-Counts bleiben unveraendert und beschreiben weiterhin den gesamten Store-Bestand. Die Quick-Filter auf /status behalten den aktuellen q-Wert beim Klick, damit der Textfilter nicht verloren geht wenn man z.B. von "All screens" auf "Stale reports" wechselt. Tests: FiltersByScreenIDSubstring, ScreenIDFilterIsCaseInsensitive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
57e0cdb43c
commit
8243eb10c9
3 changed files with 70 additions and 5 deletions
|
|
@ -158,6 +158,8 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc {
|
|||
func buildScreenStatusOverview(store playerStatusStore, query url.Values) (screenStatusOverview, error) {
|
||||
records := store.List()
|
||||
|
||||
screenIDFilter := strings.ToLower(strings.TrimSpace(query.Get("q")))
|
||||
|
||||
wantConnectivity := strings.TrimSpace(query.Get("server_connectivity"))
|
||||
switch wantConnectivity {
|
||||
case "", "online", "degraded", "offline", "unknown":
|
||||
|
|
@ -202,6 +204,9 @@ func buildScreenStatusOverview(store playerStatusStore, query url.Values) (scree
|
|||
if records[i].Stale {
|
||||
overview.Summary.Stale++
|
||||
}
|
||||
if screenIDFilter != "" && !strings.Contains(strings.ToLower(records[i].ScreenID), screenIDFilter) {
|
||||
continue
|
||||
}
|
||||
if updatedSince != nil && !isUpdatedSince(records[i], *updatedSince) {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -579,6 +579,55 @@ func TestHandleListLatestPlayerStatusesAppliesLimit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandleListLatestPlayerStatusesFiltersByScreenIDSubstring(t *testing.T) {
|
||||
store := newInMemoryPlayerStatusStore()
|
||||
store.Save(playerStatusRecord{ScreenID: "info01-dev", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "online"})
|
||||
store.Save(playerStatusRecord{ScreenID: "info02-dev", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "online"})
|
||||
store.Save(playerStatusRecord{ScreenID: "lobby-main", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "online"})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/status?q=info", 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 := len(response.Screens), 2; got != want {
|
||||
t.Fatalf("len(response.Screens) = %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleListLatestPlayerStatusesScreenIDFilterIsCaseInsensitive(t *testing.T) {
|
||||
store := newInMemoryPlayerStatusStore()
|
||||
store.Save(playerStatusRecord{ScreenID: "INFO01-DEV", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "online"})
|
||||
store.Save(playerStatusRecord{ScreenID: "lobby-main", Timestamp: "2026-03-22T16:00:00Z", Status: "running", ServerConnectivity: "online"})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/status?q=info01", 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 := len(response.Screens), 1; got != want {
|
||||
t.Fatalf("len(response.Screens) = %d, want %d", got, want)
|
||||
}
|
||||
|
||||
if got, want := response.Screens[0].ScreenID, "INFO01-DEV"; got != want {
|
||||
t.Fatalf("response.Screens[0].ScreenID = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleListLatestPlayerStatusesRejectsInvalidServerConnectivity(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/status?server_connectivity=garbage", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ type statusPageData struct {
|
|||
}
|
||||
|
||||
type statusPageFilters struct {
|
||||
ScreenIDFilter string
|
||||
ServerConnectivity string
|
||||
Stale string
|
||||
UpdatedSince string
|
||||
|
|
@ -499,6 +500,11 @@ var statusPageTemplate = template.Must(template.New("status-page").Funcs(statusT
|
|||
</div>
|
||||
|
||||
<form class="filter-form" method="get" action="{{.StatusPagePath}}">
|
||||
<div class="field full">
|
||||
<label for="q">Screen ID contains</label>
|
||||
<input id="q" name="q" type="text" placeholder="e.g. info01" value="{{.Filters.ScreenIDFilter}}">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="server_connectivity">Server connectivity</label>
|
||||
<select id="server_connectivity" name="server_connectivity">
|
||||
|
|
@ -786,6 +792,7 @@ func handleScreenDetailPage(store playerStatusStore) http.HandlerFunc {
|
|||
|
||||
func buildStatusPageData(store playerStatusStore, query url.Values, overview screenStatusOverview) statusPageData {
|
||||
filters := statusPageFilters{
|
||||
ScreenIDFilter: strings.TrimSpace(query.Get("q")),
|
||||
ServerConnectivity: strings.TrimSpace(query.Get("server_connectivity")),
|
||||
Stale: strings.TrimSpace(query.Get("stale")),
|
||||
UpdatedSince: strings.TrimSpace(query.Get("updated_since")),
|
||||
|
|
@ -804,34 +811,35 @@ func buildStatusPageData(store playerStatusStore, query url.Values, overview scr
|
|||
}
|
||||
|
||||
func buildStatusQuickFilters(filters statusPageFilters) []statusFilterLink {
|
||||
base := statusPageFilters{ScreenIDFilter: filters.ScreenIDFilter, Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}
|
||||
return []statusFilterLink{
|
||||
{
|
||||
Label: "All screens",
|
||||
Href: buildStatusPageHref(statusPageFilters{Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}),
|
||||
Href: buildStatusPageHref(base),
|
||||
Class: "",
|
||||
Active: filters.ServerConnectivity == "" && filters.Stale == "",
|
||||
},
|
||||
{
|
||||
Label: "Connectivity offline",
|
||||
Href: buildStatusPageHref(statusPageFilters{ServerConnectivity: "offline", Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}),
|
||||
Href: buildStatusPageHref(statusPageFilters{ScreenIDFilter: base.ScreenIDFilter, ServerConnectivity: "offline", Limit: base.Limit, UpdatedSince: base.UpdatedSince}),
|
||||
Class: "offline",
|
||||
Active: filters.ServerConnectivity == "offline" && filters.Stale == "",
|
||||
},
|
||||
{
|
||||
Label: "Connectivity degraded",
|
||||
Href: buildStatusPageHref(statusPageFilters{ServerConnectivity: "degraded", Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}),
|
||||
Href: buildStatusPageHref(statusPageFilters{ScreenIDFilter: base.ScreenIDFilter, ServerConnectivity: "degraded", Limit: base.Limit, UpdatedSince: base.UpdatedSince}),
|
||||
Class: "degraded",
|
||||
Active: filters.ServerConnectivity == "degraded" && filters.Stale == "",
|
||||
},
|
||||
{
|
||||
Label: "Stale reports",
|
||||
Href: buildStatusPageHref(statusPageFilters{Stale: "true", Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}),
|
||||
Href: buildStatusPageHref(statusPageFilters{ScreenIDFilter: base.ScreenIDFilter, Stale: "true", Limit: base.Limit, UpdatedSince: base.UpdatedSince}),
|
||||
Class: "",
|
||||
Active: filters.ServerConnectivity == "" && filters.Stale == "true",
|
||||
},
|
||||
{
|
||||
Label: "Fresh reports",
|
||||
Href: buildStatusPageHref(statusPageFilters{Stale: "false", Limit: filters.Limit, UpdatedSince: filters.UpdatedSince}),
|
||||
Href: buildStatusPageHref(statusPageFilters{ScreenIDFilter: base.ScreenIDFilter, Stale: "false", Limit: base.Limit, UpdatedSince: base.UpdatedSince}),
|
||||
Class: "online",
|
||||
Active: filters.ServerConnectivity == "" && filters.Stale == "false",
|
||||
},
|
||||
|
|
@ -844,6 +852,9 @@ func buildStatusPageHref(filters statusPageFilters) string {
|
|||
|
||||
func buildOverviewPath(basePath string, filters statusPageFilters) string {
|
||||
query := url.Values{}
|
||||
if filters.ScreenIDFilter != "" {
|
||||
query.Set("q", filters.ScreenIDFilter)
|
||||
}
|
||||
if filters.ServerConnectivity != "" {
|
||||
query.Set("server_connectivity", filters.ServerConnectivity)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue