- 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.
96 lines
2.9 KiB
Go
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)
|
|
}
|