slack5-1770544098/services/preferences-api/internal/domain/preferences.go
rdev-worker a31f57382b
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
build: /implement-feature user-preferences
2026-02-08 10:47:23 +00:00

80 lines
2.0 KiB
Go

package domain
import (
"fmt"
"regexp"
"time"
)
// Allowed preference keys.
var allowedKeys = map[string]bool{
"theme": true,
"language": true,
"notifications_enabled": true,
}
// Valid theme values.
var validThemes = map[string]bool{
"light": true,
"dark": true,
}
// languagePattern matches ISO 639-1 codes (two lowercase letters).
var languagePattern = regexp.MustCompile(`^[a-z]{2}$`)
// UserPreferences represents a user's stored preferences.
type UserPreferences struct {
UserID string
Preferences map[string]any
CreatedAt time.Time
UpdatedAt time.Time
}
// ValidatePreferences validates all keys and values in a preferences map.
func ValidatePreferences(prefs map[string]any) error {
for key, value := range prefs {
if err := ValidatePreferenceKey(key); err != nil {
return err
}
if err := ValidatePreferenceValue(key, value); err != nil {
return err
}
}
return nil
}
// ValidatePreferenceKey checks that the key is in the allowed set.
func ValidatePreferenceKey(key string) error {
if !allowedKeys[key] {
return fmt.Errorf("%w: %s", ErrInvalidPreferenceKey, key)
}
return nil
}
// ValidatePreferenceValue checks that the value is valid for the given key.
func ValidatePreferenceValue(key string, value any) error {
switch key {
case "theme":
s, ok := value.(string)
if !ok {
return fmt.Errorf("%w: theme must be a string", ErrInvalidPreferenceValue)
}
if !validThemes[s] {
return fmt.Errorf("%w: theme must be \"light\" or \"dark\", got %q", ErrInvalidPreferenceValue, s)
}
case "language":
s, ok := value.(string)
if !ok {
return fmt.Errorf("%w: language must be a string", ErrInvalidPreferenceValue)
}
if !languagePattern.MatchString(s) {
return fmt.Errorf("%w: language must be a valid ISO 639-1 code (e.g. \"en\"), got %q", ErrInvalidPreferenceValue, s)
}
case "notifications_enabled":
if _, ok := value.(bool); !ok {
return fmt.Errorf("%w: notifications_enabled must be a boolean", ErrInvalidPreferenceValue)
}
}
return nil
}