Files
honeyDueAPI/migrate_handlers.py
Trey t 6dac34e373 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>
2025-12-16 13:52:08 -06:00

115 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""
Migrate Gin handlers to Echo handlers
"""
import re
import sys
def migrate_handler_file(content):
"""Migrate a single handler file from Gin to Echo"""
# 1. Import changes
content = re.sub(r'"github\.com/gin-gonic/gin"', '"github.com/labstack/echo/v4"', content)
# Add validator import if not present
if '"github.com/treytartt/casera-api/internal/validator"' not in content:
content = re.sub(
r'("github\.com/treytartt/casera-api/internal/services"\n)',
r'\1\t"github.com/treytartt/casera-api/internal/validator"\n',
content
)
# 2. Handler signatures - must return error
content = re.sub(
r'func \(h \*(\w+Handler)\) (\w+)\(c \*gin\.Context\) {',
r'func (h *\1) \2(c echo.Context) error {',
content
)
# 3. c.MustGet -> c.Get
content = re.sub(r'c\.MustGet\(', 'c.Get(', content)
# 4. Bind and validate separately
# Match ShouldBindJSON pattern and replace with Bind + Validate
def replace_bind_validate(match):
indent = match.group(1)
var_name = match.group(2)
error_block = match.group(3)
# Replace the error block to use 'return'
error_block_fixed = re.sub(r'\n(\t+)c\.JSON\(', r'\n\1return c.JSON(', error_block)
error_block_fixed = re.sub(r'\n(\t+)\}\n(\t+)return\n', r'\n\1}\n', error_block_fixed)
return (f'{indent}if err := c.Bind(&{var_name}); err != nil {{\n{error_block_fixed}'
f'{indent}}}\n'
f'{indent}if err := c.Validate(&{var_name}); err != nil {{\n'
f'{indent}\treturn c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))\n'
f'{indent}}}')
# Handle ShouldBindJSON with error handling
content = re.sub(
r'(\t+)if err := c\.ShouldBindJSON\(&(\w+)\); err != nil \{((?:\n(?:\1\t.*|))*\n\1\}\n\1\treturn\n)',
replace_bind_validate,
content
)
# Handle optional ShouldBindJSON (no error check)
content = re.sub(r'c\.ShouldBindJSON\(&(\w+)\)', r'c.Bind(&\1)', content)
# 5. gin.H -> map[string]interface{}
content = re.sub(r'gin\.H\{', 'map[string]interface{}{', content)
# 6. c.Query -> c.QueryParam
content = re.sub(r'c\.Query\(', 'c.QueryParam(', content)
# 7. c.PostForm -> c.FormValue
content = re.sub(r'c\.PostForm\(', 'c.FormValue(', content)
# 8. c.GetHeader -> c.Request().Header.Get
content = re.sub(r'c\.GetHeader\(', 'c.Request().Header.Get(', content)
# 9. c.Request.Context() -> c.Request().Context()
content = re.sub(r'c\.Request\.Context\(\)', 'c.Request().Context()', content)
# 10. All c.JSON, c.Status calls must have 'return'
# Match c.JSON without return
content = re.sub(
r'(\n\t+)c\.JSON\(([^)]+\))',
r'\1return c.JSON(\2',
content
)
# Match c.Status -> c.NoContent
content = re.sub(
r'(\n\t+)c\.Status\(([^)]+)\)',
r'\1return c.NoContent(\2)',
content
)
# 11. Fix double 'return return' issues
content = re.sub(r'return return c\.', 'return c.', content)
# 12. Remove standalone 'return' at end of functions (now returns values)
# This is tricky - we need to remove lines that are just '\treturn\n}' at function end
content = re.sub(r'\n\treturn\n\}$', r'\n}', content, flags=re.MULTILINE)
content = re.sub(r'\n(\t+)return\n(\1)\}', r'\n\2}', content)
return content
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: migrate_handlers.py <handler_file.go>")
sys.exit(1)
filename = sys.argv[1]
with open(filename, 'r') as f:
content = f.read()
migrated = migrate_handler_file(content)
with open(filename, 'w') as f:
f.write(migrated)
print(f"Migrated {filename}")