veza/veza-backend-api/internal/handlers/error_response.go

136 lines
4 KiB
Go

package handlers
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"veza-backend-api/internal/errors"
)
// ErrorResponse représente le format d'erreur standardisé selon ORIGIN_API_SPECIFICATION
// GO-014: Harmonisation format erreurs HTTP
type ErrorResponse struct {
Error struct {
Code int `json:"code"`
Message string `json:"message"`
Details []errors.ErrorDetail `json:"details,omitempty"`
RequestID string `json:"request_id,omitempty"`
Timestamp string `json:"timestamp"`
Context map[string]interface{} `json:"context,omitempty"`
} `json:"error"`
}
// RespondWithAppError répond avec une AppError au format standardisé ORIGIN_API_SPECIFICATION
// GO-014: Harmonisation format erreurs HTTP selon ORIGIN_API_SPECIFICATION
func RespondWithAppError(c *gin.Context, appErr *errors.AppError) {
statusCode := mapErrorCodeToHTTPStatus(appErr.Code)
errorData := struct {
Code int `json:"code"`
Message string `json:"message"`
Details []errors.ErrorDetail `json:"details,omitempty"`
RequestID string `json:"request_id,omitempty"`
Timestamp string `json:"timestamp"`
Context map[string]interface{} `json:"context,omitempty"`
}{
Code: int(appErr.Code),
Message: appErr.Message,
Details: appErr.Details,
RequestID: c.GetString("request_id"),
Timestamp: time.Now().UTC().Format(time.RFC3339),
Context: appErr.Context,
}
c.JSON(statusCode, APIResponse{
Success: false,
Data: nil,
Error: errorData,
})
}
// RespondWithError répond avec un code d'erreur et un message au format standardisé
// GO-014: Harmonisation format erreurs HTTP selon ORIGIN_API_SPECIFICATION
func RespondWithError(c *gin.Context, code int, message string, details ...errors.ErrorDetail) {
statusCode := mapErrorCodeToHTTPStatus(errors.ErrorCode(code))
errorData := struct {
Code int `json:"code"`
Message string `json:"message"`
Details []errors.ErrorDetail `json:"details,omitempty"`
RequestID string `json:"request_id,omitempty"`
Timestamp string `json:"timestamp"`
}{
Code: code,
Message: message,
Details: details,
RequestID: c.GetString("request_id"),
Timestamp: time.Now().UTC().Format(time.RFC3339),
}
c.JSON(statusCode, APIResponse{
Success: false,
Data: nil,
Error: errorData,
})
}
// mapErrorCodeToHTTPStatus mappe les codes d'erreur ORIGIN vers les codes HTTP
// GO-014: Harmonisation format erreurs HTTP selon ORIGIN_API_SPECIFICATION
func mapErrorCodeToHTTPStatus(code errors.ErrorCode) int {
// Authentication & Authorization (1000-1999)
if code >= 1000 && code < 2000 {
switch code {
case 1000, 1001, 1002, 1007, 1008: // Invalid credentials, token expired/invalid, 2FA
return http.StatusUnauthorized
case 1003, 1004, 1005, 1006: // Insufficient permissions, account issues
return http.StatusForbidden
default:
return http.StatusUnauthorized
}
}
// Validation Errors (2000-2999)
if code >= 2000 && code < 3000 {
return http.StatusBadRequest
}
// Resource Errors (3000-3999)
if code >= 3000 && code < 4000 {
switch code {
case 3000, 3003: // Not found, deleted
return http.StatusNotFound
case 3001, 3002: // Already exists, conflict
return http.StatusConflict
case 3004: // Locked
return http.StatusLocked
case 3005: // Quota exceeded
return http.StatusForbidden
default:
return http.StatusNotFound
}
}
// Business Logic Errors (4000-4999)
if code >= 4000 && code < 5000 {
return http.StatusUnprocessableEntity
}
// Rate Limiting (5000-5099)
if code >= 5000 && code < 5100 {
return http.StatusTooManyRequests
}
// External Services (6000-6999)
if code >= 6000 && code < 7000 {
return http.StatusBadGateway
}
// Internal Errors (9000-9999)
if code >= 9000 && code < 10000 {
return http.StatusInternalServerError
}
// Default
return http.StatusInternalServerError
}