create CMS error handling
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
objectIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{24}$`)
|
||||
filePathPattern = regexp.MustCompile(`^/[^/]*/v\d+/files/[a-fA-F0-9]{24}(/download)?$`)
|
||||
)
|
||||
|
||||
// ValidationService registers CMS-specific govalidator rules.
|
||||
type ValidationService struct{}
|
||||
|
||||
// NewValidationService registers custom validators used by CMS forms.
|
||||
func NewValidationService() ValidationService {
|
||||
govalidator.CustomTypeTagMap.Set("mediaRef", govalidator.CustomTypeValidator(func(i interface{}, _ interface{}) bool {
|
||||
value, ok := i.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return isMediaReference(value)
|
||||
}))
|
||||
|
||||
return ValidationService{}
|
||||
}
|
||||
|
||||
func isMediaReference(value string) bool {
|
||||
if value == "" {
|
||||
return true
|
||||
}
|
||||
if govalidator.IsURL(value) {
|
||||
return true
|
||||
}
|
||||
if objectIDPattern.MatchString(value) {
|
||||
return true
|
||||
}
|
||||
return filePathPattern.MatchString(value)
|
||||
}
|
||||
|
||||
// ValidateCMSForm validates a CMS create/update payload.
|
||||
func ValidateCMSForm(form *CMSForm) error {
|
||||
_, err := govalidator.ValidateStruct(form)
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseForm binds and validates a CMS form from the request.
|
||||
func ParseForm(c echo.Context) (*CMSForm, error) {
|
||||
var form CMSForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
return nil, fmt.Errorf("invalid request format: %w", err)
|
||||
}
|
||||
if err := ValidateCMSForm(&form); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &form, nil
|
||||
}
|
||||
|
||||
// FormatValidationErrors converts govalidator errors into user-facing messages.
|
||||
func FormatValidationErrors(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
errs, ok := err.(govalidator.Errors)
|
||||
if !ok {
|
||||
return humanizeValidationError(err.Error())
|
||||
}
|
||||
|
||||
messages := make([]string, 0, len(errs))
|
||||
for _, item := range errs {
|
||||
messages = append(messages, humanizeValidationError(item.Error()))
|
||||
}
|
||||
return strings.Join(messages, "; ")
|
||||
}
|
||||
|
||||
func humanizeValidationError(raw string) string {
|
||||
field, rule, ok := parseGovalidatorError(raw)
|
||||
if !ok {
|
||||
return raw
|
||||
}
|
||||
|
||||
label := fieldLabel(field)
|
||||
lowerRule := strings.ToLower(rule)
|
||||
|
||||
switch {
|
||||
case strings.Contains(lowerRule, "mediaref"):
|
||||
return label + " must be a valid URL or uploaded file"
|
||||
case strings.Contains(lowerRule, "url"):
|
||||
return label + " must be a valid URL"
|
||||
case strings.Contains(lowerRule, "email"):
|
||||
return label + " must be a valid email address"
|
||||
case strings.Contains(lowerRule, "length"):
|
||||
return label + " has an invalid length"
|
||||
case strings.Contains(lowerRule, "range"):
|
||||
return label + " is out of the allowed range"
|
||||
case strings.Contains(lowerRule, "in("):
|
||||
return label + " has an invalid value"
|
||||
default:
|
||||
return label + " is invalid"
|
||||
}
|
||||
}
|
||||
|
||||
func parseGovalidatorError(raw string) (field, rule string, ok bool) {
|
||||
parts := strings.SplitN(raw, ": ", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", "", false
|
||||
}
|
||||
return parts[0], parts[1], true
|
||||
}
|
||||
|
||||
func fieldLabel(field string) string {
|
||||
field = strings.TrimPrefix(field, "CMSForm.")
|
||||
replacer := strings.NewReplacer(
|
||||
"Hero.", "Hero section: ",
|
||||
"Chart.", "Chart section: ",
|
||||
"Features.", "Features section: ",
|
||||
"Challenges.", "Challenges section: ",
|
||||
"Advantages.", "Advantages section: ",
|
||||
"Contact.", "Contact section: ",
|
||||
"Footer.", "Footer section: ",
|
||||
".", " ",
|
||||
)
|
||||
label := replacer.Replace(field)
|
||||
label = strings.ReplaceAll(label, "Cards 0", "card 1")
|
||||
label = strings.ReplaceAll(label, "Cards 1", "card 2")
|
||||
label = strings.ReplaceAll(label, "Cards 2", "card 3")
|
||||
label = strings.ReplaceAll(label, "Cards 3", "card 4")
|
||||
label = strings.ReplaceAll(label, "Cards 4", "card 5")
|
||||
return label
|
||||
}
|
||||
Reference in New Issue
Block a user