From 025c10ac1f8ced30f3a66e210df097e643c1220c Mon Sep 17 00:00:00 2001 From: juanatsap Date: Thu, 20 Nov 2025 17:35:58 +0000 Subject: [PATCH] docs: Add comprehensive backend handler documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create public-facing documentation explaining backend architecture: New Documentation: - doc/14-BACKEND-HANDLERS.md (900+ lines) * Handler architecture and file organization * Request/response type system with examples * Middleware pattern and preferences handling * Comprehensive testing strategy * Data flow diagrams and best practices * Code examples for all major patterns Updated: - doc/README.md * Add Backend Handlers to technical implementation section * Update total active docs count (13 → 14) * Add quick navigation links Content Coverage: - Handler responsibilities (pages, PDF, HTMX) - Type-safe request handling with validation - Middleware architecture and context usage - Test coverage across all handler types - Request processing flow diagrams - Best practices with do/don't examples Audience: - Backend developers - API consumers - New contributors - Technical documentation readers Complements: - Educational docs in _go-learning/refactorings/ - Internal architecture documentation - API reference guide --- doc/14-BACKEND-HANDLERS.md | 566 +++++++++++++++++++++++++++++++++++++ doc/README.md | 4 +- 2 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 doc/14-BACKEND-HANDLERS.md diff --git a/doc/14-BACKEND-HANDLERS.md b/doc/14-BACKEND-HANDLERS.md new file mode 100644 index 0000000..3e7653f --- /dev/null +++ b/doc/14-BACKEND-HANDLERS.md @@ -0,0 +1,566 @@ +# Backend Handler Architecture + +**Last Updated**: November 20, 2024 + +## Overview + +This document explains how the backend handles HTTP requests, focusing on the handler architecture, type safety, middleware pattern, and testing strategy implemented in the CV website. + +## Table of Contents + +1. [Handler Architecture](#handler-architecture) +2. [Request/Response Types](#requestresponse-types) +3. [Middleware Pattern](#middleware-pattern) +4. [Testing Strategy](#testing-strategy) +5. [Data Flow](#data-flow) +6. [Best Practices](#best-practices) + +--- + +## Handler Architecture + +### File Organization + +The handler layer is organized by responsibility into focused files: + +``` +internal/handlers/ +├── cv.go # Core handler struct and constructor +├── cv_pages.go # Page rendering handlers +├── cv_pdf.go # PDF export handler +├── cv_htmx.go # HTMX toggle handlers +├── cv_helpers.go # Shared helper functions +├── types.go # Request/response types +├── errors.go # Error handling utilities +└── *_test.go # Comprehensive test suites +``` + +### Handler Responsibilities + +#### 1. Page Handlers (cv_pages.go) + +**Purpose**: Render full HTML pages and content sections + +```go +// Home - Renders the complete CV page with all content +func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) + +// CVContent - Renders CV content for HTMX swaps +func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) + +// DefaultCVShortcut - Handles shortcut URLs like /cv-jamr-2025-en.pdf +func (h *CVHandler) DefaultCVShortcut(w http.ResponseWriter, r *http.Request) +``` + +**Example Flow**: +``` +Browser Request → Home() → prepareTemplateData() → Render HTML → Response +``` + +#### 2. PDF Handler (cv_pdf.go) + +**Purpose**: Generate PDF exports with customizable options + +```go +// ExportPDF - Generates PDF with parameters: lang, length, icons, version +func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) +``` + +**Features**: +- Multi-language support (English, Spanish) +- Length variants (short, long) +- Icon visibility toggle (show, hide) +- Theme variants (default with skills, clean without skills) +- Smart filename generation +- Print-optimized CSS rendering + +**Example Request**: +```bash +GET /export-pdf?lang=es&length=long&icons=show&version=with_skills +``` + +#### 3. HTMX Toggle Handlers (cv_htmx.go) + +**Purpose**: Handle interactive toggles via HTMX + +```go +// ToggleLength - Toggle between short and long CV +func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request) + +// ToggleIcons - Show/hide skill and tool icons +func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request) + +// SwitchLanguage - Switch between English and Spanish +func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request) + +// ToggleTheme - Toggle between default and clean theme +func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request) +``` + +**HTMX Pattern**: +1. User clicks toggle button +2. HTMX sends POST request +3. Handler updates cookie +4. Handler returns HTML fragment with out-of-band swaps +5. HTMX swaps multiple DOM elements atomically + +--- + +## Request/Response Types + +### Type-Safe Request Handling + +Instead of manually parsing query parameters, we use structured types with validation: + +#### PDF Export Request + +```go +// PDFExportRequest represents all PDF export parameters +type PDFExportRequest struct { + Lang string // "en" or "es" + Length string // "short" or "long" + Icons string // "show" or "hide" + Version string // "with_skills" or "clean" +} + +// Parse and validate in one call +req, err := ParsePDFExportRequest(r) +if err != nil { + // Return 400 Bad Request with clear error message + HandleError(w, r, BadRequestError(err.Error())) + return +} + +// Type-safe access +filename := fmt.Sprintf("cv-%s-%s.pdf", req.Length, req.Lang) +``` + +#### Benefits + +✅ **Type Safety**: Compile-time guarantees prevent typos +✅ **Self-Documenting**: Struct fields show all valid parameters +✅ **Centralized Validation**: One place to update validation rules +✅ **Clear Errors**: Descriptive error messages for invalid requests + +**Example Validation**: + +```go +// Automatic validation with helpful error messages +GET /export-pdf?lang=fr +→ 400 Bad Request: "unsupported language: fr (use 'en' or 'es')" + +GET /export-pdf?length=medium +→ 400 Bad Request: "unsupported length: medium (use 'short' or 'long')" +``` + +#### Language Request + +```go +// LanguageRequest for endpoints that only need language +type LanguageRequest struct { + Lang string // "en" or "es" +} + +// Usage +req, err := ParseLanguageRequest(r) +// Defaults to "en" if not specified +// Validates against supported languages +``` + +--- + +## Middleware Pattern + +### Preferences Middleware + +**Purpose**: Read user preferences from cookies once and make them available via context + +#### Architecture + +``` +Request + ↓ +PreferencesMiddleware + ├─ Read all preference cookies + ├─ Migrate old values (extended → long, true → show) + ├─ Store in request context + └─ Pass to next handler + ↓ +Handler + ├─ Get preferences from context + ├─ No cookie reading needed + └─ Use preferences in business logic + ↓ +Response +``` + +#### Implementation + +```go +// Middleware reads cookies and stores in context +func PreferencesMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + prefs := &Preferences{ + CVLength: getPreferenceCookie(r, "cv-length", "short"), + CVIcons: getPreferenceCookie(r, "cv-icons", "show"), + CVLanguage: getPreferenceCookie(r, "cv-language", "en"), + CVTheme: getPreferenceCookie(r, "cv-theme", "default"), + ColorTheme: getPreferenceCookie(r, "color-theme", "light"), + } + + // Automatic migration of old preference values + if prefs.CVLength == "extended" { + prefs.CVLength = "long" + } + + // Store in context for handlers + ctx := context.WithValue(r.Context(), PreferencesKey, prefs) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +// Handlers access preferences via context +func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) { + // Get preferences from context (already read by middleware) + prefs := middleware.GetPreferences(r) + + // Use preferences + cvLengthClass := "cv-short" + if prefs.CVLength == "long" { + cvLengthClass = "cv-long" + } +} +``` + +#### Benefits + +✅ **Performance**: Cookies read once per request +✅ **Consistency**: All handlers get same preference values +✅ **Maintainability**: Migration logic in one place +✅ **Testability**: Easy to mock preferences via context + +--- + +## Testing Strategy + +### Test Coverage + +The handler layer has comprehensive test coverage across multiple files: + +``` +internal/handlers/ +├── cv_pages_test.go # Page handler tests +├── cv_htmx_test.go # HTMX toggle tests +├── pdf_test.go # PDF generation tests (integration) +└── cv_security_test.go # Security validation tests +``` + +### Page Handler Tests + +**File**: `cv_pages_test.go` +**Test Cases**: 15+ +**Coverage**: Language validation, rendering, shortcuts + +```go +// Example test structure +func TestHome(t *testing.T) { + tests := []struct { + name string + lang string + expectStatus int + expectContains string + }{ + { + name: "Default language (English)", + lang: "", + expectStatus: http.StatusOK, + expectContains: "Juan Andrés Moreno Rubio", + }, + { + name: "Invalid language", + lang: "fr", + expectStatus: http.StatusBadRequest, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Test implementation + }) + } +} +``` + +### HTMX Handler Tests + +**File**: `cv_htmx_test.go` +**Test Cases**: 20+ +**Coverage**: Toggles, cookies, method validation, migrations + +```go +// Example: Testing toggle behavior +func TestToggleLength(t *testing.T) { + tests := []struct { + name string + currentLength string + expectedToggle string + }{ + { + name: "Toggle from short to long", + currentLength: "short", + expectedToggle: "long", + }, + { + name: "Migration: extended → long", + currentLength: "extended", + expectedToggle: "short", // extended becomes long, then toggles + }, + } + // ... +} +``` + +### Method Validation Tests + +All HTMX endpoints enforce POST-only requests: + +```go +func TestHTMXHandlersRequirePost(t *testing.T) { + // Tests verify GET requests return 405 Method Not Allowed + handlers := []struct { + name string + handler func(http.ResponseWriter, *http.Request) + }{ + {"ToggleLength", handler.ToggleLength}, + {"ToggleIcons", handler.ToggleIcons}, + {"ToggleTheme", handler.ToggleTheme}, + } + + // All should reject GET with 405 + for _, h := range handlers { + req := httptest.NewRequest(http.MethodGet, "/endpoint", nil) + w := httptest.NewRecorder() + h.handler(w, req) + assert.Equal(t, http.StatusMethodNotAllowed, w.Code) + } +} +``` + +### Running Tests + +```bash +# Run all unit tests (excludes PDF generation) +go test -short ./... + +# Run specific handler tests +go test -short ./internal/handlers/... -v + +# Run all tests including integration tests +make test-all + +# Pre-commit hook runs tests automatically +git commit -m "changes" # Tests run before commit +``` + +--- + +## Data Flow + +### Request Processing Flow + +``` +1. Client Request + ├─ Browser/HTMX makes HTTP request + └─ URL: /export-pdf?lang=es&length=long + +2. Middleware Chain + ├─ Recovery (catch panics) + ├─ Logger (request logging) + ├─ Security Headers (CSP, HSTS) + └─ PreferencesMiddleware (read cookies) + +3. Router + ├─ Match URL pattern + └─ Dispatch to handler + +4. Handler + ├─ Parse request (type-safe) + │ └─ ParsePDFExportRequest(r) + ├─ Validate parameters + │ └─ Return 400 if invalid + ├─ Prepare data + │ └─ prepareTemplateData(lang) + ├─ Generate response + │ └─ Render template or generate PDF + └─ Return response + +5. Client Response + ├─ HTML page + ├─ HTMX fragment + ├─ PDF download + └─ Error page +``` + +### Template Data Preparation + +Central helper function used by multiple handlers: + +```go +func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) { + // Load CV data (cached) + cv, err := cvmodel.LoadCV(lang) + if err != nil { + return nil, err + } + + // Load UI translations (cached) + ui, err := uimodel.LoadUI(lang) + if err != nil { + return nil, err + } + + // Calculate dynamic data + for i := range cv.Experience { + cv.Experience[i].Duration = calculateDuration( + cv.Experience[i].StartDate, + cv.Experience[i].EndDate, + cv.Experience[i].Current, + lang, + ) + } + + // Process projects + for i := range cv.Projects { + processProjectDates(&cv.Projects[i], lang) + } + + // Prepare skills + skillsLeft, skillsRight := splitSkills(cv.Skills.Technical) + + // Return complete data map + return map[string]interface{}{ + "CV": cv, + "UI": ui, + "Lang": lang, + "SkillsLeft": skillsLeft, + "SkillsRight": skillsRight, + "YearsOfExperience": calculateYearsOfExperience(), + "CurrentYear": time.Now().Year(), + "CanonicalURL": fmt.Sprintf("https://juan.andres.morenorub.io/?lang=%s", lang), + "AlternateEN": "https://juan.andres.morenorub.io/?lang=en", + "AlternateES": "https://juan.andres.morenorub.io/?lang=es", + }, nil +} +``` + +--- + +## Best Practices + +### 1. Type-Safe Requests + +✅ **DO**: Use structured request types +```go +req, err := ParsePDFExportRequest(r) +if err != nil { + HandleError(w, r, BadRequestError(err.Error())) + return +} +``` + +❌ **DON'T**: Manually parse parameters +```go +lang := r.URL.Query().Get("lang") +if lang == "" { lang = "en" } +if lang != "en" && lang != "es" { + // Repetitive validation code +} +``` + +### 2. Centralized Validation + +✅ **DO**: Validate in request parser +```go +func ParsePDFExportRequest(r *http.Request) (*PDFExportRequest, error) { + req := &PDFExportRequest{ /* parse */ } + + // All validation in one place + if req.Lang != "en" && req.Lang != "es" { + return nil, fmt.Errorf("unsupported language: %s", req.Lang) + } + return req, nil +} +``` + +❌ **DON'T**: Scatter validation across handlers +```go +// Validation duplicated in multiple places +``` + +### 3. Reuse Helper Functions + +✅ **DO**: Use shared data preparation +```go +func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) { + data, err := h.prepareTemplateData(lang) + // Add page-specific fields +} + +func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) { + data, err := h.prepareTemplateData(lang) + // Reuse same data preparation +} +``` + +❌ **DON'T**: Duplicate data preparation logic +```go +// 100+ lines duplicated across handlers +``` + +### 4. Test All Handlers + +✅ **DO**: Write comprehensive tests +```go +func TestToggleLength(t *testing.T) { + // Test toggle behavior + // Test cookie persistence + // Test migration from old values +} +``` + +✅ **DO**: Test error cases +```go +func TestInvalidLanguage(t *testing.T) { + // Verify 400 Bad Request + // Check error message +} +``` + +### 5. Use Middleware for Cross-Cutting Concerns + +✅ **DO**: Extract common logic to middleware +```go +// PreferencesMiddleware reads cookies once +// Handlers get preferences from context +``` + +❌ **DON'T**: Read cookies in every handler +```go +// Cookie reading duplicated across handlers +``` + +--- + +## Related Documentation + +- [Architecture Overview](./1-ARCHITECTURE.md) - System architecture patterns +- [API Reference](./3-API.md) - Complete API documentation +- [Security](./9-SECURITY.md) - Security implementation details +- [PDF Export](./11-PDF-EXPORT.md) - PDF generation details +- [Testing Guide](../_go-learning/refactorings/) - Detailed refactoring documentation + +--- + +## Changelog + +- **Nov 20, 2024**: Initial documentation covering handler refactoring, type safety, middleware pattern, and testing strategy diff --git a/doc/README.md b/doc/README.md index 7022496..c76a74a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -18,6 +18,7 @@ - [5. Zoom Implementation](5-ZOOM-IMPLEMENTATION.md) - Custom zoom feature technical details - [12. CSS Architecture](12-CSS-ARCHITECTURE.md) - Modular CSS structure and ITCSS organization ⭐ - [13. Toast Notifications](13-TOAST-NOTIFICATIONS.md) - Toast notification system for PDF downloads and user feedback +- [14. Backend Handlers](14-BACKEND-HANDLERS.md) - Handler architecture, type safety, middleware, and testing ⭐ **Deployment & Operations** - [8. Deployment Guide](8-DEPLOYMENT.md) - Production deployment instructions @@ -46,6 +47,7 @@ | 5 | [ZOOM_IMPLEMENTATION.md](5-ZOOM-IMPLEMENTATION.md) | Zoom feature implementation details | Feature developers | | 12 | [CSS-ARCHITECTURE.md](12-CSS-ARCHITECTURE.md) | Modular CSS structure, ITCSS layers, HTMX integration | Frontend developers, designers | | 13 | [TOAST-NOTIFICATIONS.md](13-TOAST-NOTIFICATIONS.md) | Toast notification system, PDF download feedback, user notifications | Frontend developers, UX designers | +| 14 | [BACKEND-HANDLERS.md](14-BACKEND-HANDLERS.md) | Handler architecture, type safety, middleware pattern, testing strategy | Backend developers | ### User & Operations Documentation @@ -141,4 +143,4 @@ All documentation in this project follows these standards: **Last Updated**: 2025-11-20 **Documentation Status**: ✅ Clean, organized, zero redundancy -**Total Active Docs**: 13 core documents + archive +**Total Active Docs**: 14 core documents + archive