api-qobiltu-dev / validation /custom_rules.go
lifedebugger's picture
Deploy files from GitHub repository
158bb31
package validation
import (
"strings"
"sync"
v10 "github.com/go-playground/validator/v10"
"gorm.io/gorm"
)
type ValidOptionSource interface {
GetValidOptions(key string) ([]string, error)
GetValidKeys() []string
}
// --------------------
// InMemoryOptionSource
// --------------------
type InMemoryOptionSource struct{}
var inMemoryOptions = map[string][]string{
"last_education": {"SD", "SMP", "SMA", "D1", "D2", "D3", "D4", "D5", "S1", "S2", "S3"},
"marital_status": {"Belum Menikah", "Duda", "Janda"},
"gender": {"Laki-laki", "Perempuan"},
"monthly_expenses": {"< 2 Juta", "2-5 Juta", "5-20 Juta", "> 10 Juta"},
"monthly_income": {"< 3 Juta", "3-5 Juta", "5-10 Juta", "> 10 Juta"},
"religion": {"Islam", "Non-Islam"},
"family_role": {"Ayah", "Ibu", "Kakak", "Adik", "Anak"},
"life_status": {"Hidup", "Wafat"},
"body_shape": {"Ideal", "Kurus", "Berisi", "Gemuk"},
"skin_color": {"Putih", "Kuning Langsat", "Sawo Matang"},
"hair_type": {"Lurus", "Bergelombang", "Keriting"},
"frequently": {"Selalu", "Sering", "Kadang", "Jarang"},
"quran_reading_ability": {"Lancar", "Menengah", "Perlu Bimbingan"},
}
func (s *InMemoryOptionSource) GetValidOptions(key string) ([]string, error) {
return inMemoryOptions[key], nil
}
func (s *InMemoryOptionSource) GetValidKeys() []string {
keys := make([]string, 0, len(inMemoryOptions))
for k := range inMemoryOptions {
keys = append(keys, k)
}
return keys
}
// --------------------
// DBOptionSource
// --------------------
type DBOptionSource struct {
options map[string][]string
mu sync.RWMutex
}
type (
OptionCategory struct {
ID int64 `gorm:"primaryKey" json:"id"`
OptionName string `json:"option_name"`
OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
}
OptionValues struct {
ID int64 `gorm:"primaryKey" json:"id"`
OptionCategoryID int64 `json:"option_category_id"`
OptionValue string `json:"option_value"`
}
)
func NewDBOptionSource(db *gorm.DB) (*DBOptionSource, error) {
var categories []OptionCategory
if err := db.Find(&categories).Error; err != nil {
return nil, err
}
options := make(map[string][]string)
for _, cat := range categories {
var values []OptionValues
if err := db.Where("option_category_id = ?", cat.ID).Find(&values).Error; err != nil {
return nil, err
}
for _, val := range values {
options[cat.OptionSlug] = append(options[cat.OptionSlug], val.OptionValue)
}
}
return &DBOptionSource{options: options}, nil
}
func (s *DBOptionSource) GetValidOptions(key string) ([]string, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return s.options[key], nil
}
func (s *DBOptionSource) GetValidKeys() []string {
s.mu.RLock()
defer s.mu.RUnlock()
keys := make([]string, 0, len(s.options))
for k := range s.options {
keys = append(keys, k)
}
return keys
}
// --------------------
// Validator
// --------------------
type Validator struct {
source ValidOptionSource
}
func NewValidatorRules(source ValidOptionSource) *Validator {
return &Validator{source: source}
}
func (v *Validator) GenericOptionRule(key string) func(fl v10.FieldLevel) bool {
return func(fl v10.FieldLevel) bool {
value := fl.Field().String()
if value == "" {
return true
}
validOptions, err := v.source.GetValidOptions(key)
if err != nil {
return false
}
for _, opt := range validOptions {
if opt == value {
return true
}
}
return false
}
}
func (v *Validator) RegisterAllCustomRules(validate *v10.Validate) error {
for _, key := range v.source.GetValidKeys() {
err := validate.RegisterValidation(key, v.GenericOptionRule(key))
if err != nil {
return err
}
}
err := validate.RegisterValidation("password", v.PasswordRule)
if err != nil {
return err
}
return nil
}
func (v *Validator) PasswordRule(fl v10.FieldLevel) bool {
password := fl.Field().String()
return len(password) >= 8 &&
strings.ContainsAny(password, "abcdefghijklmnopqrstuvwxyz") &&
strings.ContainsAny(password, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") &&
strings.ContainsAny(password, "0123456789")
}