Files

96 lines
2.9 KiB
Go
Raw Permalink Normal View History

2026-07-30 09:51:25 +07:00
package validator
import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/gin-gonic/gin/binding"
validatorlib "github.com/go-playground/validator/v10"
)
// FieldError merepresentasikan 1 field DTO yang gagal validasi.
// Field mengikuti nama di tag `json` DTO (bukan nama struct Go), supaya konsisten
// dengan nama yang dikirim client di body request.
type FieldError struct {
Field string `json:"field"`
Message string `json:"message"`
}
// RegisterJSONTagNameFunc membuat validator Gin melaporkan error pakai nama tag `json`
// dari DTO (misal "email", "password"), bukan nama struct Go (misal "Email", "Password").
// WAJIB dipanggil sekali saat startup aplikasi (lihat internal/router/router.go),
// sebelum ada request yang divalidasi.
func RegisterJSONTagNameFunc() {
v, ok := binding.Validator.Engine().(*validatorlib.Validate)
if !ok {
return
}
v.RegisterTagNameFunc(func(field reflect.StructField) string {
name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
if name == "-" || name == "" {
return field.Name
}
return name
})
}
func TranslateError(err error) []FieldError {
var validationErrs validatorlib.ValidationErrors
if !errors.As(err, &validationErrs) {
return []FieldError{{
Field: "body",
Message: "format request tidak valid, pastikan body berupa JSON yang benar",
}}
}
fieldErrors := make([]FieldError, 0, len(validationErrs))
for _, fe := range validationErrs {
fieldErrors = append(fieldErrors, FieldError{
Field: fe.Field(),
Message: buildMessage(fe),
})
}
return fieldErrors
}
// buildMessage menerjemahkan tag validasi (required, email, min, dst) jadi kalimat
// Bahasa Indonesia yang jelas. Tambahkan case baru di sini kalau ada tag validasi baru
// yang dipakai di DTO tapi belum ada terjemahannya.
func buildMessage(fe validatorlib.FieldError) string {
field := fe.Field()
switch fe.Tag() {
case "required":
return fmt.Sprintf("%s wajib diisi", field)
case "email":
return fmt.Sprintf("%s harus berupa alamat email yang valid", field)
case "min":
if fe.Kind() == reflect.String {
return fmt.Sprintf("%s minimal %s karakter", field, fe.Param())
}
return fmt.Sprintf("%s minimal bernilai %s", field, fe.Param())
case "max":
if fe.Kind() == reflect.String {
return fmt.Sprintf("%s maksimal %s karakter", field, fe.Param())
}
return fmt.Sprintf("%s maksimal bernilai %s", field, fe.Param())
case "gte":
return fmt.Sprintf("%s minimal bernilai %s", field, fe.Param())
case "lte":
return fmt.Sprintf("%s maksimal bernilai %s", field, fe.Param())
case "oneof":
return fmt.Sprintf("%s harus salah satu dari: %s", field, fe.Param())
case "len":
return fmt.Sprintf("%s harus tepat %s karakter", field, fe.Param())
case "numeric":
return fmt.Sprintf("%s harus berupa angka", field)
case "alphanum":
return fmt.Sprintf("%s hanya boleh berisi huruf dan angka", field)
default:
return fmt.Sprintf("%s tidak valid", field)
}
}