mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-11-25 09:21:11 +02:00
109 lines
2.8 KiB
Go
109 lines
2.8 KiB
Go
package apis
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
|
"github.com/pocketbase/pocketbase/tools/inflector"
|
|
)
|
|
|
|
// ApiError defines the struct 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
|
|
}
|
|
|
|
// RawData returns the unformatted error data (could be an internal error, text, etc.)
|
|
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
|
|
}
|