Migrate from Gin to Echo framework and add comprehensive integration tests
Major changes: - Migrate all handlers from Gin to Echo framework - Add new apperrors, echohelpers, and validator packages - Update middleware for Echo compatibility - Add ArchivedHandler to task categorization chain (archived tasks go to cancelled_tasks column) - Add 6 new integration tests: - RecurringTaskLifecycle: NextDueDate advancement for weekly/monthly tasks - MultiUserSharing: Complex sharing with user removal - TaskStateTransitions: All state transitions and kanban column changes - DateBoundaryEdgeCases: Threshold boundary testing - CascadeOperations: Residence deletion cascade effects - MultiUserOperations: Shared residence collaboration - Add single-purpose repository functions for kanban columns (GetOverdueTasks, GetDueSoonTasks, etc.) - Fix RemoveUser route param mismatch (userId -> user_id) - Fix determineExpectedColumn helper to correctly prioritize in_progress over overdue 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
66
internal/apperrors/handler.go
Normal file
66
internal/apperrors/handler.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package apperrors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/dto/responses"
|
||||
"github.com/treytartt/casera-api/internal/i18n"
|
||||
customvalidator "github.com/treytartt/casera-api/internal/validator"
|
||||
)
|
||||
|
||||
// HTTPErrorHandler handles all errors returned from handlers in a consistent way.
|
||||
// It converts AppErrors, validation errors, and Echo HTTPErrors to JSON responses.
|
||||
// This is the base handler - additional service-level error handling can be added in router.go.
|
||||
func HTTPErrorHandler(err error, c echo.Context) {
|
||||
// Already committed? Skip
|
||||
if c.Response().Committed {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle AppError (our custom application errors)
|
||||
var appErr *AppError
|
||||
if errors.As(err, &appErr) {
|
||||
message := i18n.LocalizedMessage(c, appErr.MessageKey)
|
||||
// If i18n key not found (returns the key itself), use fallback message
|
||||
if message == appErr.MessageKey && appErr.Message != "" {
|
||||
message = appErr.Message
|
||||
} else if message == appErr.MessageKey {
|
||||
message = appErr.MessageKey // Use the key as last resort
|
||||
}
|
||||
|
||||
// Log internal errors
|
||||
if appErr.Err != nil {
|
||||
log.Error().Err(appErr.Err).Str("message_key", appErr.MessageKey).Msg("Application error")
|
||||
}
|
||||
|
||||
c.JSON(appErr.Code, responses.ErrorResponse{Error: message})
|
||||
return
|
||||
}
|
||||
|
||||
// Handle validation errors from go-playground/validator
|
||||
var validationErrs validator.ValidationErrors
|
||||
if errors.As(err, &validationErrs) {
|
||||
c.JSON(http.StatusBadRequest, customvalidator.FormatValidationErrors(err))
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Echo's built-in HTTPError
|
||||
var httpErr *echo.HTTPError
|
||||
if errors.As(err, &httpErr) {
|
||||
msg := fmt.Sprintf("%v", httpErr.Message)
|
||||
c.JSON(httpErr.Code, responses.ErrorResponse{Error: msg})
|
||||
return
|
||||
}
|
||||
|
||||
// Default: Internal server error (don't expose error details to client)
|
||||
log.Error().Err(err).Msg("Unhandled error")
|
||||
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{
|
||||
Error: i18n.LocalizedMessage(c, "error.internal"),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user