Automated Action 887703b6a2 Add Go Todo App implementation
This commit adds a complete Go implementation of the Todo application. The
application uses Gin framework for the web server, GORM for database access,
and SQLite for storage.

Key features:
- Todo CRUD operations with the same API endpoints
- Health check endpoint
- Database migrations
- Tests for models, services, and API handlers
- Documentation for the API
- Configurable settings
2025-05-17 22:20:05 +00:00

150 lines
3.7 KiB
Go

package api
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/simpletodoapp/go-todo-app/internal/dto"
"github.com/simpletodoapp/go-todo-app/internal/service"
)
// TodoHandler handles todo-related API requests
type TodoHandler struct {
todoService *service.TodoService
}
// NewTodoHandler creates a new todo handler
func NewTodoHandler(todoService *service.TodoService) *TodoHandler {
return &TodoHandler{todoService: todoService}
}
// RegisterRoutes registers the todo routes
func (h *TodoHandler) RegisterRoutes(router *gin.RouterGroup) {
todos := router.Group("/todos")
{
todos.GET("", h.GetTodos)
todos.POST("", h.CreateTodo)
todos.GET("/:id", h.GetTodo)
todos.PUT("/:id", h.UpdateTodo)
todos.DELETE("/:id", h.DeleteTodo)
}
}
// GetTodos handles GET /api/todos
// Returns a list of todos with optional filtering and pagination
func (h *TodoHandler) GetTodos(c *gin.Context) {
// Parse query parameters
skip, _ := strconv.Atoi(c.DefaultQuery("skip", "0"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
var completed *bool
if completedStr, exists := c.GetQuery("completed"); exists {
completedBool, err := strconv.ParseBool(completedStr)
if err == nil {
completed = &completedBool
}
}
// Get todos from service
todos, err := h.todoService.GetTodos(skip, limit, completed)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, todos)
}
// GetTodo handles GET /api/todos/:id
// Returns a specific todo by ID
func (h *TodoHandler) GetTodo(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}
todo, err := h.todoService.GetTodoByID(uint(id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if todo == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Todo not found"})
return
}
c.JSON(http.StatusOK, todo)
}
// CreateTodo handles POST /api/todos
// Creates a new todo
func (h *TodoHandler) CreateTodo(c *gin.Context) {
var todoCreate dto.TodoCreate
if err := c.ShouldBindJSON(&todoCreate); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
todo, err := h.todoService.CreateTodo(todoCreate)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, todo)
}
// UpdateTodo handles PUT /api/todos/:id
// Updates an existing todo
func (h *TodoHandler) UpdateTodo(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}
var todoUpdate dto.TodoUpdate
if err := c.ShouldBindJSON(&todoUpdate); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
todo, err := h.todoService.UpdateTodo(uint(id), todoUpdate)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if todo == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Todo not found"})
return
}
c.JSON(http.StatusOK, todo)
}
// DeleteTodo handles DELETE /api/todos/:id
// Deletes a todo
func (h *TodoHandler) DeleteTodo(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}
deleted, err := h.todoService.DeleteTodo(uint(id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if !deleted {
c.JSON(http.StatusNotFound, gin.H{"error": "Todo not found"})
return
}
c.Status(http.StatusNoContent)
}