Files
DocNest/backend/internal/handlers/errors.go
M1ngdaXie 8ae7fd96e8 feat: Implement error handling and response structure for API
- Added standardized error response structure in `errors.go` for consistent error handling across the API.
- Implemented specific response functions for various HTTP status codes (400, 401, 403, 404, 500) to enhance error reporting.
- Introduced validation error handling to provide detailed feedback on input validation issues.

test: Add comprehensive tests for share handler functionality

- Created a suite of tests for share handler endpoints, covering scenarios for creating, listing, deleting shares, and managing share links.
- Included tests for permission checks, validation errors, and edge cases such as unauthorized access and invalid document IDs.

chore: Set up test utilities and database for integration tests

- Established a base handler suite for common setup tasks in tests, including database initialization and teardown.
- Implemented test data seeding to facilitate consistent testing across different scenarios.

migration: Add public sharing support in the database schema

- Modified the `documents` table to include `share_token` and `is_public` columns for managing public document sharing.
- Added constraints to ensure data integrity, preventing public documents from lacking a share token.
2026-01-05 15:25:46 -08:00

96 lines
2.9 KiB
Go

package handlers
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
// ErrorResponse represents a standardized error response
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message,omitempty"`
Details map[string]interface{} `json:"details,omitempty"`
}
// respondWithError sends a standardized error response
func respondWithError(c *gin.Context, statusCode int, errorType string, message string) {
c.JSON(statusCode, ErrorResponse{
Error: errorType,
Message: message,
})
}
// respondWithValidationError parses validation errors and sends detailed response
func respondWithValidationError(c *gin.Context, err error) {
details := make(map[string]interface{})
// Try to parse validator errors
if validationErrors, ok := err.(validator.ValidationErrors); ok {
for _, fieldError := range validationErrors {
fieldName := fieldError.Field()
switch fieldError.Tag() {
case "required":
details[fieldName] = "This field is required"
case "oneof":
details[fieldName] = fmt.Sprintf("Must be one of: %s", fieldError.Param())
case "email":
details[fieldName] = "Must be a valid email address"
case "uuid":
details[fieldName] = "Must be a valid UUID"
default:
details[fieldName] = fmt.Sprintf("Validation failed on '%s'", fieldError.Tag())
}
}
} else {
// Generic validation error
details["message"] = err.Error()
}
c.JSON(http.StatusBadRequest, ErrorResponse{
Error: "validation_error",
Message: "Invalid input",
Details: details,
})
}
// respondBadRequest sends a 400 Bad Request error
func respondBadRequest(c *gin.Context, message string) {
respondWithError(c, http.StatusBadRequest, "bad_request", message)
}
// respondUnauthorized sends a 401 Unauthorized error
func respondUnauthorized(c *gin.Context, message string) {
respondWithError(c, http.StatusUnauthorized, "unauthorized", message)
}
// respondForbidden sends a 403 Forbidden error
func respondForbidden(c *gin.Context, message string) {
respondWithError(c, http.StatusForbidden, "forbidden", message)
}
// respondNotFound sends a 404 Not Found error
func respondNotFound(c *gin.Context, resource string) {
message := fmt.Sprintf("%s not found", resource)
if resource == "" {
message = "Resource not found"
}
respondWithError(c, http.StatusNotFound, "not_found", message)
}
// respondInternalError sends a 500 Internal Server Error
// In production, you may want to log the actual error but not expose it to clients
func respondInternalError(c *gin.Context, message string, err error) {
// Log the actual error for debugging (you can replace with proper logging)
if err != nil {
fmt.Printf("Internal error: %v\n", err)
}
respondWithError(c, http.StatusInternalServerError, "internal_error", message)
}
func respondInvalidID(c *gin.Context, message string) {
respondWithError(c, http.StatusBadRequest, "invalid_id", message)
}