From 4c04dcb15ea46bfc1a9cfd24f0cab145169153af Mon Sep 17 00:00:00 2001 From: Michael Stahnke Date: Tue, 17 Feb 2026 21:35:35 -0600 Subject: [PATCH] refactor: extract parseClientFilter() helper DRY up identical client filter parsing block that was duplicated across links, quotes, and search handlers. --- internal/handler/api_v1_helpers.go | 20 ++++++++++++++++++++ internal/handler/api_v1_links.go | 13 ++----------- internal/handler/api_v1_quotes.go | 13 ++----------- internal/handler/api_v1_search.go | 15 ++------------- 4 files changed, 26 insertions(+), 35 deletions(-) diff --git a/internal/handler/api_v1_helpers.go b/internal/handler/api_v1_helpers.go index a2f2ad4..e4d7692 100644 --- a/internal/handler/api_v1_helpers.go +++ b/internal/handler/api_v1_helpers.go @@ -7,6 +7,8 @@ import ( "net/http" "strconv" "strings" + + "tumble/internal/data" ) // writeJSON writes a JSON response with the given status code and data. @@ -127,6 +129,24 @@ func trimFormatSuffix(path string) string { return path } +// parseClientFilter extracts client filter parameters from the request query string. +func parseClientFilter(r *http.Request) (data.ClientFilter, error) { + var f data.ClientFilter + if st := r.URL.Query().Get("client_type"); st != "" { + f.ClientType = &st + } + if sn := r.URL.Query().Get("client_network"); sn != "" { + f.ClientNetwork = &sn + } + if sc := r.URL.Query().Get("client_channel"); sc != "" { + f.ClientChannel = &sc + } + if err := f.Validate(); err != nil { + return f, err + } + return f, nil +} + // isAuthorizedAPIKey checks if the request has a valid API key. // Uses X-API-Key header only (no query param auth - per design doc). // Always allows localhost requests. diff --git a/internal/handler/api_v1_links.go b/internal/handler/api_v1_links.go index 3774052..80a4e18 100644 --- a/internal/handler/api_v1_links.go +++ b/internal/handler/api_v1_links.go @@ -71,17 +71,8 @@ func (h *Handler) apiV1ListLinks(w http.ResponseWriter, r *http.Request) { offset := parseIntParam(r, "offset", 0, 1000000) // Parse client filter query params - var clientFilter data.ClientFilter - if st := r.URL.Query().Get("client_type"); st != "" { - clientFilter.ClientType = &st - } - if sn := r.URL.Query().Get("client_network"); sn != "" { - clientFilter.ClientNetwork = &sn - } - if sc := r.URL.Query().Get("client_channel"); sc != "" { - clientFilter.ClientChannel = &sc - } - if err := clientFilter.Validate(); err != nil { + clientFilter, err := parseClientFilter(r) + if err != nil { writeAPIError(w, http.StatusBadRequest, "invalid_params", err.Error()) return } diff --git a/internal/handler/api_v1_quotes.go b/internal/handler/api_v1_quotes.go index deefe8f..d46dedf 100644 --- a/internal/handler/api_v1_quotes.go +++ b/internal/handler/api_v1_quotes.go @@ -71,17 +71,8 @@ func (h *Handler) apiV1ListQuotes(w http.ResponseWriter, r *http.Request) { offset := parseIntParam(r, "offset", 0, 1000000) // Parse client filter query params - var clientFilter data.ClientFilter - if st := r.URL.Query().Get("client_type"); st != "" { - clientFilter.ClientType = &st - } - if sn := r.URL.Query().Get("client_network"); sn != "" { - clientFilter.ClientNetwork = &sn - } - if sc := r.URL.Query().Get("client_channel"); sc != "" { - clientFilter.ClientChannel = &sc - } - if err := clientFilter.Validate(); err != nil { + clientFilter, err := parseClientFilter(r) + if err != nil { writeAPIError(w, http.StatusBadRequest, "invalid_params", err.Error()) return } diff --git a/internal/handler/api_v1_search.go b/internal/handler/api_v1_search.go index 84e91d2..b889a9f 100644 --- a/internal/handler/api_v1_search.go +++ b/internal/handler/api_v1_search.go @@ -3,8 +3,6 @@ package handler import ( "net/http" "strings" - - "tumble/internal/data" ) // APIv1SearchHandler handles GET /api/v1/search @@ -64,17 +62,8 @@ func (h *Handler) APIv1SearchHandler(w http.ResponseWriter, r *http.Request) { offset := parseIntParam(r, "offset", 0, 1000000) // Parse client filter query params - var clientFilter data.ClientFilter - if st := r.URL.Query().Get("client_type"); st != "" { - clientFilter.ClientType = &st - } - if sn := r.URL.Query().Get("client_network"); sn != "" { - clientFilter.ClientNetwork = &sn - } - if sc := r.URL.Query().Get("client_channel"); sc != "" { - clientFilter.ClientChannel = &sc - } - if err := clientFilter.Validate(); err != nil { + clientFilter, err := parseClientFilter(r) + if err != nil { writeAPIError(w, http.StatusBadRequest, "invalid_params", err.Error()) return } -- 2.51.2