backend-ci.yml's `test -z "$(gofmt -l .)"` strict gate (added in
13c21ac11) failed on a backlog of unformatted files. None of the
85 files in this commit had been edited since the gate was added
because no push touched veza-backend-api/** in between, so the
gate never fired until today's CI fixes triggered it.
The diff is exclusively whitespace alignment in struct literals
and trailing-space comments. `go build ./...` and the full test
suite (with VEZA_SKIP_INTEGRATION=1 -short) pass identically.
82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
apperrors "veza-backend-api/internal/errors"
|
|
"veza-backend-api/internal/services"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
var SearchHandlersInstance *SearchHandlers
|
|
|
|
// SearchServiceInterface defines the interface for search operations
|
|
// This allows for easier testing with mocks
|
|
type SearchServiceInterface interface {
|
|
Search(query string, types []string) (*services.SearchResult, error)
|
|
Suggestions(query string, limit int) (*services.SearchResult, error)
|
|
}
|
|
|
|
type SearchHandlers struct {
|
|
searchService SearchServiceInterface
|
|
}
|
|
|
|
func NewSearchHandlers(searchService *services.SearchService) {
|
|
SearchHandlersInstance = &SearchHandlers{
|
|
searchService: searchService,
|
|
}
|
|
}
|
|
|
|
// NewSearchHandlersWithInterface creates new search handlers with an interface (for testing)
|
|
func NewSearchHandlersWithInterface(searchService SearchServiceInterface) *SearchHandlers {
|
|
SearchHandlersInstance = &SearchHandlers{
|
|
searchService: searchService,
|
|
}
|
|
return SearchHandlersInstance
|
|
}
|
|
|
|
// Search performs a full-text search across tracks, users, and playlists
|
|
func (sh *SearchHandlers) Search(c *gin.Context) {
|
|
query := c.Query("q")
|
|
if query == "" {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Search query is required"))
|
|
return
|
|
}
|
|
|
|
types := c.QueryArray("type")
|
|
|
|
results, err := sh.searchService.Search(query, types)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Search failed", err))
|
|
return
|
|
}
|
|
|
|
RespondSuccess(c, http.StatusOK, results)
|
|
}
|
|
|
|
// Suggestions returns autocomplete suggestions for the search input
|
|
func (sh *SearchHandlers) Suggestions(c *gin.Context) {
|
|
query := c.Query("q")
|
|
if query == "" {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Query parameter 'q' is required"))
|
|
return
|
|
}
|
|
limit := 5
|
|
if l := c.Query("limit"); l != "" {
|
|
if n, err := parseInt(l); err == nil && n > 0 && n <= 20 {
|
|
limit = n
|
|
}
|
|
}
|
|
results, err := sh.searchService.Suggestions(query, limit)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewInternalErrorWrap("Suggestions failed", err))
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, results)
|
|
}
|
|
|
|
func parseInt(s string) (int, error) {
|
|
return strconv.Atoi(s)
|
|
}
|