8a709c6863
Five complementary improvements to handler layer: 1. Fix Pre-Commit Hook - Remove broken Perl-style regex (unsupported by Go) - Use -short flag to exclude integration tests - Tests now run successfully in pre-commit 2. Extract Duplicate Logic - Remove 100+ lines of duplicate data preparation - Both Home() and CVContent() now use prepareTemplateData() - Reduce cv_pages.go from 290 to 120 lines (58% reduction) 3. Request/Response Types - Create internal/handlers/types.go with structured types - PDFExportRequest, LanguageRequest, PreferenceToggleRequest - Type-safe parameter parsing with centralized validation - Refactor ExportPDF to use typed requests 4. Middleware Extraction - Create internal/middleware/preferences.go - PreferencesMiddleware reads cookies once, stores in context - Automatic migration of old preference values - Ready for integration in routes 5. Handler Tests - Add internal/handlers/cv_pages_test.go (190 lines, 15+ cases) - Add internal/handlers/cv_htmx_test.go (325 lines, 20+ cases) - Test language validation, toggles, cookies, methods - Increase handler test coverage significantly Testing: - All unit tests pass (35+ new test cases) - Pre-commit hook working - Build succeeds - No breaking changes Benefits: - Type safety: Compile-time parameter validation - Code quality: 170 lines of duplication eliminated - Testing: 100% increase in test files - Architecture: Clean middleware pattern - Developer experience: Self-documenting request types Documentation: - Create _go-learning/refactorings/004-handler-improvements.md - Document all five improvements with examples - Include metrics, testing strategy, and future improvements
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// REQUEST/RESPONSE TYPES
|
|
// Structured types for common request patterns with validation
|
|
// ==============================================================================
|
|
|
|
// LanguageRequest represents a request with language parameter
|
|
type LanguageRequest struct {
|
|
Lang string
|
|
}
|
|
|
|
// ParseLanguageRequest parses and validates language from query parameters
|
|
func ParseLanguageRequest(r *http.Request) (*LanguageRequest, error) {
|
|
lang := r.URL.Query().Get("lang")
|
|
if lang == "" {
|
|
lang = "en"
|
|
}
|
|
|
|
// Validate language
|
|
if lang != "en" && lang != "es" {
|
|
return nil, fmt.Errorf("unsupported language: %s (use 'en' or 'es')", lang)
|
|
}
|
|
|
|
return &LanguageRequest{Lang: lang}, nil
|
|
}
|
|
|
|
// PDFExportRequest represents all parameters for PDF export
|
|
type PDFExportRequest struct {
|
|
Lang string // Language: "en" or "es"
|
|
Length string // Length: "short" or "long"
|
|
Icons string // Icons: "show" or "hide"
|
|
Version string // Version: "with_skills" or "clean"
|
|
}
|
|
|
|
// ParsePDFExportRequest parses and validates PDF export parameters
|
|
func ParsePDFExportRequest(r *http.Request) (*PDFExportRequest, error) {
|
|
req := &PDFExportRequest{
|
|
Lang: r.URL.Query().Get("lang"),
|
|
Length: r.URL.Query().Get("length"),
|
|
Icons: r.URL.Query().Get("icons"),
|
|
Version: r.URL.Query().Get("version"),
|
|
}
|
|
|
|
// Set defaults
|
|
if req.Lang == "" {
|
|
req.Lang = "en"
|
|
}
|
|
if req.Length == "" {
|
|
req.Length = "short"
|
|
}
|
|
if req.Icons == "" {
|
|
req.Icons = "show"
|
|
}
|
|
if req.Version == "" {
|
|
req.Version = "with_skills"
|
|
}
|
|
|
|
// Validate language
|
|
if req.Lang != "en" && req.Lang != "es" {
|
|
return nil, fmt.Errorf("unsupported language: %s (use 'en' or 'es')", req.Lang)
|
|
}
|
|
|
|
// Validate length
|
|
if req.Length != "short" && req.Length != "long" {
|
|
return nil, fmt.Errorf("unsupported length: %s (use 'short' or 'long')", req.Length)
|
|
}
|
|
|
|
// Validate icons
|
|
if req.Icons != "show" && req.Icons != "hide" {
|
|
return nil, fmt.Errorf("unsupported icons option: %s (use 'show' or 'hide')", req.Icons)
|
|
}
|
|
|
|
// Validate version
|
|
if req.Version != "with_skills" && req.Version != "clean" {
|
|
return nil, fmt.Errorf("unsupported version: %s (use 'with_skills' or 'clean')", req.Version)
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// PreferenceToggleRequest represents a toggle request with language context
|
|
type PreferenceToggleRequest struct {
|
|
Lang string // Current language from query or cookie
|
|
}
|
|
|
|
// ParsePreferenceToggleRequest parses toggle request parameters
|
|
func ParsePreferenceToggleRequest(r *http.Request, defaultLang string) *PreferenceToggleRequest {
|
|
lang := r.URL.Query().Get("lang")
|
|
if lang == "" {
|
|
lang = defaultLang
|
|
}
|
|
|
|
return &PreferenceToggleRequest{Lang: lang}
|
|
}
|