Files
cv-site/internal/handlers/types.go
T
juanatsap 4528e04bad feat: Complete all remaining Future Improvements (#4-8)
Implemented 5 additional architectural improvements:

1. Response Types (types.go)
   - APIResponse with Success, Data, Error, Meta fields
   - ErrorInfo with Code, Message, Field, Details
   - MetaInfo with Timestamp, Version, RequestID
   - SuccessResponse() and NewErrorResponse() helpers
   - HealthCheckResponse for health endpoint
   - Consistent JSON API responses

2. Validation Tags (types.go)
   - Added struct tags to LanguageRequest
   - Added struct tags to PDFExportRequest
   - Declarative validation rules (oneof, required)
   - Self-documenting validation constraints
   - Ready for go-playground/validator integration

3. Context Helper Functions (middleware/preferences.go)
   - GetLanguage(), GetCVLength(), GetCVIcons(), GetCVTheme(), GetColorTheme()
   - IsLongCV(), IsShortCV() boolean helpers
   - ShowIcons(), HideIcons() boolean helpers
   - IsCleanTheme(), IsDefaultTheme() boolean helpers
   - IsDarkMode(), IsLightMode() boolean helpers
   - 13 new convenience functions for cleaner code

4. Typed Errors (errors.go)
   - ErrorCode constants for all error types
   - DomainError with Code, Message, Err, StatusCode, Field
   - Unwrap() support for error chains
   - WithError() and WithField() fluent builders
   - InvalidLanguageError(), InvalidLengthError(), etc.
   - PDFGenerationError(), MethodNotAllowedError(), RateLimitError()
   - 13 error codes, domain-specific constructors

5. Benchmark Tests
   - handlers/benchmarks_test.go (11 benchmarks)
   - middleware/benchmarks_test.go (12 benchmarks)
   - Sequential benchmarks for handlers, middleware, request parsing
   - Parallel benchmarks for concurrent load testing
   - Response creation benchmarks
   - Helper function benchmarks

Benefits:
- Type Safety: Validation tags and structured types
- Developer Experience: 13 context helpers reduce boilerplate
- Error Handling: Domain-specific errors with codes
- Performance Monitoring: 23 benchmarks for regression detection
- API Consistency: Standardized response formats
- Maintainability: Self-documenting validation and errors

Testing:
- All unit tests pass
- All benchmarks working
- Build succeeds
- No breaking changes
2025-11-20 18:05:45 +00:00

168 lines
5.1 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 `validate:"required,oneof=en es"`
}
// 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 `validate:"required,oneof=en es"` // Language: "en" or "es"
Length string `validate:"required,oneof=short long"` // Length: "short" or "long"
Icons string `validate:"required,oneof=show hide"` // Icons: "show" or "hide"
Version string `validate:"required,oneof=with_skills clean"` // 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}
}
// ==============================================================================
// RESPONSE TYPES
// Structured response types for consistent API responses
// ==============================================================================
// APIResponse is a standardized response wrapper for JSON responses
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error *ErrorInfo `json:"error,omitempty"`
Meta *MetaInfo `json:"meta,omitempty"`
}
// ErrorInfo provides structured error information
type ErrorInfo struct {
Code string `json:"code"` // Error code (e.g., "INVALID_LANGUAGE")
Message string `json:"message"` // Human-readable error message
Field string `json:"field,omitempty"` // Field that caused the error
Details string `json:"details,omitempty"` // Additional error details
}
// MetaInfo provides metadata about the response
type MetaInfo struct {
Timestamp int64 `json:"timestamp,omitempty"` // Unix timestamp
Version string `json:"version,omitempty"` // API version
RequestID string `json:"request_id,omitempty"` // Request tracking ID
}
// SuccessResponse creates a success response
func SuccessResponse(data interface{}) *APIResponse {
return &APIResponse{
Success: true,
Data: data,
}
}
// NewErrorResponse creates an error response
func NewErrorResponse(code, message string) *APIResponse {
return &APIResponse{
Success: false,
Error: &ErrorInfo{
Code: code,
Message: message,
},
}
}
// ErrorResponseWithField creates an error response with field information
func ErrorResponseWithField(code, message, field string) *APIResponse {
return &APIResponse{
Success: false,
Error: &ErrorInfo{
Code: code,
Message: message,
Field: field,
},
}
}
// HealthCheckResponse represents health check endpoint response
type HealthCheckResponse struct {
Status string `json:"status"` // "healthy" or "unhealthy"
Version string `json:"version"` // Application version
Uptime int64 `json:"uptime,omitempty"` // Uptime in seconds
Checks map[string]bool `json:"checks,omitempty"` // Component health checks
}