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>
4.9 KiB
4.9 KiB
Phase 4: Gin to Echo Handler Migration Status
Completed Files
✅ auth_handler.go
- Status: Fully migrated
- Methods migrated: 13 methods
- Login, Register, Logout, CurrentUser, UpdateProfile
- VerifyEmail, ResendVerification
- ForgotPassword, VerifyResetCode, ResetPassword
- AppleSignIn, GoogleSignIn
- Key changes applied:
- Import changed from gin to echo
- Added validator import
- All handlers return error
- c.Bind + c.Validate pattern implemented
- c.MustGet → c.Get
- gin.H → map[string]interface{}
- c.Request.Context() → c.Request().Context()
- All c.JSON calls use
return
Remaining Files to Migrate
🔧 residence_handler.go
- Status: Partially migrated (needs cleanup)
- Methods: 13 methods
- Issue: Sed-based automated migration created syntax errors
- Next steps: Manual cleanup needed
⏳ task_handler.go
- Methods: ~17 methods
- Complexity: High (multipart form handling for completions)
- Special considerations:
- Has multipart/form-data handling in CreateCompletion
- Multiple lookup endpoints (categories, priorities, frequencies)
⏳ contractor_handler.go
- Methods: 8 methods
- Complexity: Medium
⏳ document_handler.go
- Methods: 8 methods
- Complexity: High (multipart form handling)
- Special considerations: File upload in CreateDocument
⏳ notification_handler.go
- Methods: 9 methods
- Complexity: Medium
- Special considerations: Query parameters for pagination
⏳ subscription_handler.go
- Status: Unknown
- Estimated complexity: Medium
⏳ upload_handler.go
- Methods: 4 methods
- Complexity: Medium
- Special considerations: c.FormFile handling, c.DefaultQuery
⏳ user_handler.go
- Methods: 3 methods
- Complexity: Low
⏳ media_handler.go
- Status: Unknown
- Estimated complexity: Medium
⏳ static_data_handler.go
- Methods: Unknown
- Complexity: Low (likely just lookups)
⏳ task_template_handler.go
- Status: Unknown
- Estimated complexity: Medium
⏳ tracking_handler.go
- Status: Unknown
- Estimated complexity: Low
⏳ subscription_webhook_handler.go
- Status: Unknown
- Estimated complexity: Medium-High (webhook handling)
Migration Pattern
All handlers must follow these transformations:
// BEFORE (Gin)
func (h *Handler) Method(c *gin.Context) {
user := c.MustGet(middleware.AuthUserKey).(*models.User)
var req requests.SomeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
result, err := h.service.DoSomething(&req)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, result)
}
// AFTER (Echo)
func (h *Handler) Method(c echo.Context) error {
user := c.Get(middleware.AuthUserKey).(*models.User)
var req requests.SomeRequest
if err := c.Bind(&req); err != nil {
return c.JSON(400, map[string]interface{}{"error": err.Error()})
}
if err := c.Validate(&req); err != nil {
return c.JSON(400, validator.FormatValidationErrors(err))
}
result, err := h.service.DoSomething(&req)
if err != nil {
return c.JSON(500, map[string]interface{}{"error": err.Error()})
}
return c.JSON(200, result)
}
Critical Context Changes
| Gin | Echo |
|---|---|
c.MustGet() |
c.Get() |
c.ShouldBindJSON() |
c.Bind() + c.Validate() |
c.JSON(status, data) |
return c.JSON(status, data) |
c.Query("key") |
c.QueryParam("key") |
c.DefaultQuery("k", "v") |
Manual: if v := c.QueryParam("k"); v != "" { } else { v = "default" } |
c.PostForm("field") |
c.FormValue("field") |
c.GetHeader("X-...") |
c.Request().Header.Get("X-...") |
c.Request.Context() |
c.Request().Context() |
c.Status(code) |
return c.NoContent(code) |
gin.H{...} |
map[string]interface{}{...} |
Multipart Form Handling
For handlers with file uploads (document_handler, task_handler):
// Request parsing
c.Request.ParseMultipartForm(32 << 20) // Same
c.PostForm("field") → c.FormValue("field")
c.FormFile("file") // Same
Next Steps
- Clean up residence_handler.go manually
- Migrate contractor_handler.go (simpler, good template)
- Migrate smaller files: user_handler.go, upload_handler.go, notification_handler.go
- Migrate complex files: task_handler.go, document_handler.go
- Migrate remaining files
- Test compilation
- Update route registration (if not already done in Phase 3)
Automation Lessons Learned
- Sed-based bulk replacements are error-prone for complex Go code
- Better approach: Manual migration with copy-paste for repetitive patterns
- Python script provided in migrate_handlers.py (not yet tested)
- Best approach: Methodical manual migration with validation at each step