108 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| package rest
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	validation "github.com/go-ozzo/ozzo-validation/v4"
 | |
| 	"github.com/pocketbase/pocketbase/tools/inflector"
 | |
| )
 | |
| 
 | |
| // ApiError defines the properties for a basic api error response.
 | |
| type ApiError struct {
 | |
| 	Code    int            `json:"code"`
 | |
| 	Message string         `json:"message"`
 | |
| 	Data    map[string]any `json:"data"`
 | |
| 
 | |
| 	// stores unformatted error data (could be an internal error, text, etc.)
 | |
| 	rawData any
 | |
| }
 | |
| 
 | |
| // Error makes it compatible with the `error` interface.
 | |
| func (e *ApiError) Error() string {
 | |
| 	return e.Message
 | |
| }
 | |
| 
 | |
| func (e *ApiError) RawData() any {
 | |
| 	return e.rawData
 | |
| }
 | |
| 
 | |
| // NewNotFoundError creates and returns 404 `ApiError`.
 | |
| func NewNotFoundError(message string, data any) *ApiError {
 | |
| 	if message == "" {
 | |
| 		message = "The requested resource wasn't found."
 | |
| 	}
 | |
| 
 | |
| 	return NewApiError(http.StatusNotFound, message, data)
 | |
| }
 | |
| 
 | |
| // NewBadRequestError creates and returns 400 `ApiError`.
 | |
| func NewBadRequestError(message string, data any) *ApiError {
 | |
| 	if message == "" {
 | |
| 		message = "Something went wrong while processing your request."
 | |
| 	}
 | |
| 
 | |
| 	return NewApiError(http.StatusBadRequest, message, data)
 | |
| }
 | |
| 
 | |
| // NewForbiddenError creates and returns 403 `ApiError`.
 | |
| func NewForbiddenError(message string, data any) *ApiError {
 | |
| 	if message == "" {
 | |
| 		message = "You are not allowed to perform this request."
 | |
| 	}
 | |
| 
 | |
| 	return NewApiError(http.StatusForbidden, message, data)
 | |
| }
 | |
| 
 | |
| // NewUnauthorizedError creates and returns 401 `ApiError`.
 | |
| func NewUnauthorizedError(message string, data any) *ApiError {
 | |
| 	if message == "" {
 | |
| 		message = "Missing or invalid authentication token."
 | |
| 	}
 | |
| 
 | |
| 	return NewApiError(http.StatusUnauthorized, message, data)
 | |
| }
 | |
| 
 | |
| // NewApiError creates and returns new normalized `ApiError` instance.
 | |
| func NewApiError(status int, message string, data any) *ApiError {
 | |
| 	message = inflector.Sentenize(message)
 | |
| 
 | |
| 	formattedData := map[string]any{}
 | |
| 
 | |
| 	if v, ok := data.(validation.Errors); ok {
 | |
| 		formattedData = resolveValidationErrors(v)
 | |
| 	}
 | |
| 
 | |
| 	return &ApiError{
 | |
| 		rawData: data,
 | |
| 		Data:    formattedData,
 | |
| 		Code:    status,
 | |
| 		Message: strings.TrimSpace(message),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func resolveValidationErrors(validationErrors validation.Errors) map[string]any {
 | |
| 	result := map[string]any{}
 | |
| 
 | |
| 	// extract from each validation error its error code and message.
 | |
| 	for name, err := range validationErrors {
 | |
| 		// check for nested errors
 | |
| 		if nestedErrs, ok := err.(validation.Errors); ok {
 | |
| 			result[name] = resolveValidationErrors(nestedErrs)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		errCode := "validation_invalid_value" // default
 | |
| 		if errObj, ok := err.(validation.ErrorObject); ok {
 | |
| 			errCode = errObj.Code()
 | |
| 		}
 | |
| 
 | |
| 		result[name] = map[string]string{
 | |
| 			"code":    errCode,
 | |
| 			"message": inflector.Sentenize(err.Error()),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 |