diff --git a/EXHAUSTIVE_TODO_LIST.md b/EXHAUSTIVE_TODO_LIST.md index 4e098d5b4..2b4457f55 100644 --- a/EXHAUSTIVE_TODO_LIST.md +++ b/EXHAUSTIVE_TODO_LIST.md @@ -1664,11 +1664,20 @@ Critical path dependencies: ### Sub-Epic 5.2: Pre-Validation 🟢 #### Task 5.2.1: Add Backend Validation Endpoint -- [ ] **Action 5.2.1.1**: Create `/api/v1/validate` endpoint +- [x] **Action 5.2.1.1**: Create `/api/v1/validate` endpoint - **Scope**: `veza-backend-api/internal/handlers/validate.go` (create) - - **Dependencies**: None + - **Dependencies**: None ✅ - **Risk**: LOW - - **Validation**: Endpoint validates request bodies + - **Validation**: ✅ Endpoint validates request bodies: + - Created ValidateHandler with Validate method + - Endpoint accepts POST /api/v1/validate with type and data + - Supports RegisterRequest and LoginRequest validation types + - Uses existing validator from CommonHandler + - Returns ValidateResponse with valid flag and errors array + - Public endpoint (no auth required) + - Route registered in setupValidateRoutes + - Code compiles successfully + - Follows existing handler patterns - **Rollback**: Delete endpoint - [ ] **Action 5.2.1.2**: Frontend: Call validate before submit diff --git a/veza-backend-api/internal/api/router.go b/veza-backend-api/internal/api/router.go index 7a172189f..0c477c384 100644 --- a/veza-backend-api/internal/api/router.go +++ b/veza-backend-api/internal/api/router.go @@ -264,6 +264,9 @@ func (r *APIRouter) Setup(router *gin.Engine) error { return err } + // Action 5.2.1.1: Validation endpoint for pre-validation + r.setupValidateRoutes(v1) + // Réactivation des routes User et Track pour Phase 1 r.setupUserRoutes(v1) r.setupTrackRoutes(v1) @@ -512,6 +515,14 @@ func (r *APIRouter) setupAuthRoutes(router *gin.RouterGroup) error { return nil } +// setupValidateRoutes configures the validation endpoint +// Action 5.2.1.1: Create /api/v1/validate endpoint +func (r *APIRouter) setupValidateRoutes(router *gin.RouterGroup) { + validateHandler := handlers.NewValidateHandler(r.logger) + // Public endpoint - no auth required for validation + router.POST("/validate", validateHandler.Validate) +} + // setupInternalRoutes configure les routes internal (legacy and modern) // These routes must be on the root router, not under /api/v1 func (r *APIRouter) setupInternalRoutes(router *gin.Engine) { diff --git a/veza-backend-api/internal/handlers/validate.go b/veza-backend-api/internal/handlers/validate.go new file mode 100644 index 000000000..a8549b138 --- /dev/null +++ b/veza-backend-api/internal/handlers/validate.go @@ -0,0 +1,104 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "veza-backend-api/internal/dto" + "veza-backend-api/internal/errors" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// ValidateRequest represents the request body for the validate endpoint +type ValidateRequest struct { + Type string `json:"type" binding:"required"` // e.g., "RegisterRequest", "LoginRequest" + Data json.RawMessage `json:"data" binding:"required"` // The data to validate +} + +// ValidateResponse represents the response from the validate endpoint +type ValidateResponse struct { + Valid bool `json:"valid"` + Errors []dto.ValidationError `json:"errors,omitempty"` + Message string `json:"message,omitempty"` +} + +// ValidateHandler handles validation requests +type ValidateHandler struct { + commonHandler *CommonHandler + logger *zap.Logger +} + +// NewValidateHandler creates a new ValidateHandler +func NewValidateHandler(logger *zap.Logger) *ValidateHandler { + return &ValidateHandler{ + commonHandler: NewCommonHandler(logger), + logger: logger, + } +} + +// Validate validates request bodies against known DTOs +// POST /api/v1/validate +// @Summary Validate request body +// @Description Validates request data against known DTO types without executing the actual operation +// @Tags Validation +// @Accept json +// @Produce json +// @Param request body ValidateRequest true "Validation request with type and data" +// @Success 200 {object} ValidateResponse "Validation result" +// @Failure 400 {object} APIResponse "Invalid request format" +// @Router /validate [post] +func (h *ValidateHandler) Validate(c *gin.Context) { + var req ValidateRequest + if err := c.ShouldBindJSON(&req); err != nil { + RespondWithError(c, http.StatusBadRequest, "Invalid request format", errors.ErrorDetail{ + Message: err.Error(), + }) + return + } + + // Map type string to DTO struct and validate + var validationErrors []dto.ValidationError + + switch req.Type { + case "RegisterRequest": + var dtoReq dto.RegisterRequest + if err := json.Unmarshal(req.Data, &dtoReq); err != nil { + RespondWithError(c, http.StatusBadRequest, "Invalid data format for RegisterRequest", errors.ErrorDetail{ + Message: err.Error(), + }) + return + } + validationErrors = h.commonHandler.validator.Validate(&dtoReq) + + case "LoginRequest": + var dtoReq dto.LoginRequest + if err := json.Unmarshal(req.Data, &dtoReq); err != nil { + RespondWithError(c, http.StatusBadRequest, "Invalid data format for LoginRequest", errors.ErrorDetail{ + Message: err.Error(), + }) + return + } + validationErrors = h.commonHandler.validator.Validate(&dtoReq) + + default: + RespondWithError(c, http.StatusBadRequest, "Unknown validation type: "+req.Type) + return + } + + // Return validation result + if len(validationErrors) > 0 { + RespondSuccess(c, http.StatusOK, ValidateResponse{ + Valid: false, + Errors: validationErrors, + Message: "Validation failed", + }) + return + } + + RespondSuccess(c, http.StatusOK, ValidateResponse{ + Valid: true, + Message: "Validation passed", + }) +}