package handlers import ( "context" "net/http" "time" apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/models" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" ) // RoleServiceInterface defines the interface for role operations // This allows for easier testing with mocks type RoleServiceInterface interface { GetRoles(ctx context.Context) ([]models.Role, error) GetRole(ctx context.Context, roleID uuid.UUID) (*models.Role, error) CreateRole(ctx context.Context, role *models.Role) error UpdateRole(ctx context.Context, roleID uuid.UUID, updates *models.Role) error DeleteRole(ctx context.Context, roleID uuid.UUID) error AssignRoleToUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID, assignedBy uuid.UUID, expiresAt *time.Time) error RevokeRoleFromUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID) error GetUserRoles(ctx context.Context, userID uuid.UUID) ([]models.Role, error) } // RoleHandler gère les endpoints de gestion des rôles type RoleHandler struct { roleService RoleServiceInterface commonHandler *CommonHandler } // NewRoleHandler crée un nouveau RoleHandler func NewRoleHandler(roleService *services.RoleService, logger *zap.Logger) *RoleHandler { return &RoleHandler{ roleService: roleService, commonHandler: NewCommonHandler(logger), } } // NewRoleHandlerWithInterface crée un nouveau RoleHandler avec une interface (pour les tests) func NewRoleHandlerWithInterface(roleService RoleServiceInterface, logger *zap.Logger) *RoleHandler { return &RoleHandler{ roleService: roleService, commonHandler: NewCommonHandler(logger), } } // GetRoles récupère tous les rôles // BE-API-007: Implement roles management endpoints func (h *RoleHandler) GetRoles(c *gin.Context) { roles, err := h.roleService.GetRoles(c.Request.Context()) if err != nil { RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get roles", err)) return } RespondSuccess(c, http.StatusOK, roles) } // GetRole récupère un rôle par ID // BE-API-007: Implement roles management endpoints func (h *RoleHandler) GetRole(c *gin.Context) { roleIDStr := c.Param("id") roleID, err := uuid.Parse(roleIDStr) if err != nil { RespondWithAppError(c, apperrors.NewValidationError("invalid role id")) return } role, err := h.roleService.GetRole(c.Request.Context(), roleID) if err != nil { if err.Error() == "role not found" { RespondWithAppError(c, apperrors.NewNotFoundError("role")) } else { RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get role", err)) } return } RespondSuccess(c, http.StatusOK, role) } // CreateRole crée un nouveau rôle func (h *RoleHandler) CreateRole(c *gin.Context) { var role models.Role if appErr := h.commonHandler.BindAndValidateJSON(c, &role); appErr != nil { RespondWithAppError(c, appErr) return } if err := h.roleService.CreateRole(c.Request.Context(), &role); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"role": role}) } // UpdateRole met à jour un rôle func (h *RoleHandler) UpdateRole(c *gin.Context) { roleIDStr := c.Param("id") roleID, err := uuid.Parse(roleIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"}) return } var updates models.Role if appErr := h.commonHandler.BindAndValidateJSON(c, &updates); appErr != nil { RespondWithAppError(c, appErr) return } if err := h.roleService.UpdateRole(c.Request.Context(), roleID, &updates); err != nil { if err.Error() == "role not found or is system role" { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"message": "role updated"}) } // DeleteRole supprime un rôle func (h *RoleHandler) DeleteRole(c *gin.Context) { roleIDStr := c.Param("id") roleID, err := uuid.Parse(roleIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"}) return } if err := h.roleService.DeleteRole(c.Request.Context(), roleID); err != nil { if err.Error() == "role not found" || err.Error() == "cannot delete system role" { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, gin.H{"message": "role deleted"}) } // AssignRole assigne un rôle à un utilisateur // BE-API-007: Implement roles management endpoints // Supports both :id and :userId parameters for flexibility func (h *RoleHandler) AssignRole(c *gin.Context) { // Try :userId first (from /users/:userId/roles), fallback to :id userIDStr := c.Param("userId") if userIDStr == "" { userIDStr = c.Param("id") } userID, err := uuid.Parse(userIDStr) if err != nil { RespondWithAppError(c, apperrors.NewValidationError("invalid user id")) return } var req struct { RoleID uuid.UUID `json:"role_id" binding:"required"` ExpiresAt *time.Time `json:"expires_at"` } if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil { RespondWithAppError(c, appErr) return } // Récupérer l'ID de l'utilisateur qui assigne depuis le contexte assignedBy, ok := GetUserIDUUID(c) if !ok { return // Erreur déjà envoyée par GetUserIDUUID } if err := h.roleService.AssignRoleToUser(c.Request.Context(), userID, req.RoleID, assignedBy, req.ExpiresAt); err != nil { RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to assign role", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "role assigned"}) } // RevokeRole révoque un rôle d'un utilisateur // BE-API-007: Implement roles management endpoints // Supports both :id and :userId parameters for flexibility func (h *RoleHandler) RevokeRole(c *gin.Context) { // Try :userId first (from /users/:userId/roles/:roleId), fallback to :id userIDStr := c.Param("userId") if userIDStr == "" { userIDStr = c.Param("id") } userID, err := uuid.Parse(userIDStr) if err != nil { RespondWithAppError(c, apperrors.NewValidationError("invalid user id")) return } roleIDStr := c.Param("roleId") roleID, err := uuid.Parse(roleIDStr) if err != nil { RespondWithAppError(c, apperrors.NewValidationError("invalid role id")) return } if err := h.roleService.RevokeRoleFromUser(c.Request.Context(), userID, roleID); err != nil { if err.Error() == "role assignment not found" { RespondWithAppError(c, apperrors.NewNotFoundError("role assignment")) } else { RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to revoke role", err)) } return } RespondSuccess(c, http.StatusOK, gin.H{"message": "role revoked"}) } // GetUserRoles récupère tous les rôles d'un utilisateur func (h *RoleHandler) GetUserRoles(c *gin.Context) { userIDStr := c.Param("id") userID, err := uuid.Parse(userIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } roles, err := h.roleService.GetUserRoles(c.Request.Context(), userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"roles": roles}) }