
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
150 lines
3.7 KiB
Go
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)
|
|
} |