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
This commit is contained in:
juanatsap
2025-11-20 18:05:45 +00:00
parent ae89d84e07
commit 4528e04bad
10 changed files with 636 additions and 13 deletions
+134
View File
@@ -2,6 +2,7 @@ package handlers
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
@@ -141,3 +142,136 @@ func DataLoadError(err error, dataType string) *AppError {
true,
)
}
// ==============================================================================
// TYPED ERRORS
// Domain-specific error types for better error handling
// ==============================================================================
// ErrorCode represents a specific error condition
type ErrorCode string
const (
ErrCodeInvalidLanguage ErrorCode = "INVALID_LANGUAGE"
ErrCodeInvalidLength ErrorCode = "INVALID_LENGTH"
ErrCodeInvalidIcons ErrorCode = "INVALID_ICONS"
ErrCodeInvalidTheme ErrorCode = "INVALID_THEME"
ErrCodeInvalidVersion ErrorCode = "INVALID_VERSION"
ErrCodeTemplateNotFound ErrorCode = "TEMPLATE_NOT_FOUND"
ErrCodeTemplateRender ErrorCode = "TEMPLATE_RENDER"
ErrCodeDataLoad ErrorCode = "DATA_LOAD"
ErrCodePDFGeneration ErrorCode = "PDF_GENERATION"
ErrCodeMethodNotAllowed ErrorCode = "METHOD_NOT_ALLOWED"
ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED"
ErrCodeForbidden ErrorCode = "FORBIDDEN"
ErrCodeRateLimitExceeded ErrorCode = "RATE_LIMIT_EXCEEDED"
)
// DomainError represents a domain-specific error
type DomainError struct {
Code ErrorCode
Message string
Err error
StatusCode int
Field string // Optional field that caused the error
}
// Error implements the error interface
func (e *DomainError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Code, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
// Unwrap returns the underlying error
func (e *DomainError) Unwrap() error {
return e.Err
}
// NewDomainError creates a new domain error
func NewDomainError(code ErrorCode, message string, statusCode int) *DomainError {
return &DomainError{
Code: code,
Message: message,
StatusCode: statusCode,
}
}
// WithError adds an underlying error
func (e *DomainError) WithError(err error) *DomainError {
e.Err = err
return e
}
// WithField adds field information
func (e *DomainError) WithField(field string) *DomainError {
e.Field = field
return e
}
// Common domain error constructors
func InvalidLanguageError(lang string) *DomainError {
return NewDomainError(
ErrCodeInvalidLanguage,
fmt.Sprintf("Unsupported language: %s (use 'en' or 'es')", lang),
http.StatusBadRequest,
).WithField("lang")
}
func InvalidLengthError(length string) *DomainError {
return NewDomainError(
ErrCodeInvalidLength,
fmt.Sprintf("Unsupported length: %s (use 'short' or 'long')", length),
http.StatusBadRequest,
).WithField("length")
}
func InvalidIconsError(icons string) *DomainError {
return NewDomainError(
ErrCodeInvalidIcons,
fmt.Sprintf("Unsupported icons option: %s (use 'show' or 'hide')", icons),
http.StatusBadRequest,
).WithField("icons")
}
func InvalidThemeError(theme string) *DomainError {
return NewDomainError(
ErrCodeInvalidTheme,
fmt.Sprintf("Unsupported theme: %s (use 'default' or 'clean')", theme),
http.StatusBadRequest,
).WithField("theme")
}
func InvalidVersionError(version string) *DomainError {
return NewDomainError(
ErrCodeInvalidVersion,
fmt.Sprintf("Unsupported version: %s (use 'with_skills' or 'clean')", version),
http.StatusBadRequest,
).WithField("version")
}
func PDFGenerationError(err error) *DomainError {
return NewDomainError(
ErrCodePDFGeneration,
"Failed to generate PDF",
http.StatusInternalServerError,
).WithError(err)
}
func MethodNotAllowedError(method string) *DomainError {
return NewDomainError(
ErrCodeMethodNotAllowed,
fmt.Sprintf("Method %s not allowed", method),
http.StatusMethodNotAllowed,
)
}
func RateLimitError() *DomainError {
return NewDomainError(
ErrCodeRateLimitExceeded,
"Rate limit exceeded. Please try again later.",
http.StatusTooManyRequests,
)
}