feat: Refactor activity and project handlers to use utility functions for ID parsing and response handling

This commit is contained in:
2025-03-12 11:38:24 +00:00
parent 1198b326c1
commit 294047a2b0
7 changed files with 178 additions and 254 deletions
+136
View File
@@ -0,0 +1,136 @@
package utils
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/timetracker/backend/internal/types"
)
// ParseID parses an ID from the URL parameter and converts it to a types.ULID
func ParseID(c *gin.Context, paramName string) (types.ULID, error) {
idStr := c.Param(paramName)
return types.ULIDFromString(idStr)
}
// BindJSON binds the request body to the provided struct
func BindJSON(c *gin.Context, obj interface{}) error {
if err := c.ShouldBindJSON(obj); err != nil {
return fmt.Errorf("invalid request body: %w", err)
}
return nil
}
// ConvertToDTO converts a slice of models to a slice of DTOs using the provided conversion function
func ConvertToDTO[M any, D any](models []M, convertFn func(*M) D) []D {
dtos := make([]D, len(models))
for i, model := range models {
// Create a copy of the model to avoid issues with loop variable capture
modelCopy := model
dtos[i] = convertFn(&modelCopy)
}
return dtos
}
// HandleGetAll is a generic function to handle GET all entities endpoints
func HandleGetAll[M any, D any](
c *gin.Context,
getAllFn func(ctx context.Context) ([]M, error),
convertFn func(*M) D,
entityName string,
) {
// Get entities from the database
entities, err := getAllFn(c.Request.Context())
if err != nil {
InternalErrorResponse(c, fmt.Sprintf("Error retrieving %s: %s", entityName, err.Error()))
return
}
// Convert to DTOs
dtos := ConvertToDTO(entities, convertFn)
SuccessResponse(c, 200, dtos)
}
// HandleGetByID is a generic function to handle GET entity by ID endpoints
func HandleGetByID[M any, D any](
c *gin.Context,
getByIDFn func(ctx context.Context, id types.ULID) (*M, error),
convertFn func(*M) D,
entityName string,
) {
// Parse ID from URL
id, err := ParseID(c, "id")
if err != nil {
BadRequestResponse(c, fmt.Sprintf("Invalid %s ID format", entityName))
return
}
// Get entity from the database
entity, err := getByIDFn(c.Request.Context(), id)
if err != nil {
InternalErrorResponse(c, fmt.Sprintf("Error retrieving %s: %s", entityName, err.Error()))
return
}
if entity == nil {
NotFoundResponse(c, fmt.Sprintf("%s not found", entityName))
return
}
// Convert to DTO
dto := convertFn(entity)
SuccessResponse(c, 200, dto)
}
// HandleCreate is a generic function to handle POST entity endpoints
func HandleCreate[C any, M any, D any](
c *gin.Context,
createFn func(ctx context.Context, create C) (*M, error),
convertFn func(*M) D,
entityName string,
) {
// Parse request body
var createDTO C
if err := BindJSON(c, &createDTO); err != nil {
BadRequestResponse(c, err.Error())
return
}
// Create entity in the database
entity, err := createFn(c.Request.Context(), createDTO)
if err != nil {
InternalErrorResponse(c, fmt.Sprintf("Error creating %s: %s", entityName, err.Error()))
return
}
// Convert to DTO
dto := convertFn(entity)
SuccessResponse(c, 201, dto)
}
// HandleDelete is a generic function to handle DELETE entity endpoints
func HandleDelete(
c *gin.Context,
deleteFn func(ctx context.Context, id types.ULID) error,
entityName string,
) {
// Parse ID from URL
id, err := ParseID(c, "id")
if err != nil {
BadRequestResponse(c, fmt.Sprintf("Invalid %s ID format", entityName))
return
}
// Delete entity from the database
err = deleteFn(c.Request.Context(), id)
if err != nil {
InternalErrorResponse(c, fmt.Sprintf("Error deleting %s: %s", entityName, err.Error()))
return
}
SuccessResponse(c, 204, nil)
}