Yimaru-BackEnd/internal/domain/setting_list.go

546 lines
16 KiB
Go

package domain
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"go.uber.org/zap"
)
var (
ErrSettingNotFound = errors.New("cannot find setting in list")
)
type SettingList struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit Currency `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit Currency `json:"total_winning_limit"`
AmountForBetReferral Currency `json:"amount_for_bet_referral"`
CashbackAmountCap Currency `json:"cashback_amount_cap"`
DefaultWinningLimit int64 `json:"default_winning_limit"`
ReferralRewardAmount Currency `json:"referral_reward_amount"`
CashbackPercentage float32 `json:"cashback_percentage"`
DefaultMaxReferrals int64 `json:"default_max_referrals"`
}
type SettingListRes struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit float32 `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit float32 `json:"total_winning_limit"`
AmountForBetReferral float32 `json:"amount_for_bet_referral"`
CashbackAmountCap float32 `json:"cashback_amount_cap"`
DefaultWinningLimit int64 `json:"default_winning_limit"`
ReferralRewardAmount float32 `json:"referral_reward_amount"`
CashbackPercentage float32 `json:"cashback_percentage"`
DefaultMaxReferrals int64 `json:"default_max_referrals"`
}
func ConvertSettingListRes(settings SettingList) SettingListRes {
return SettingListRes{
SMSProvider: settings.SMSProvider,
MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes,
BetAmountLimit: settings.BetAmountLimit.Float32(),
DailyTicketPerIP: settings.DailyTicketPerIP,
TotalWinningLimit: settings.TotalWinningLimit.Float32(),
AmountForBetReferral: settings.AmountForBetReferral.Float32(),
CashbackAmountCap: settings.CashbackAmountCap.Float32(),
DefaultWinningLimit: settings.DefaultWinningLimit,
ReferralRewardAmount: settings.ReferralRewardAmount.Float32(),
CashbackPercentage: settings.CashbackPercentage,
DefaultMaxReferrals: settings.DefaultMaxReferrals,
}
}
type SaveSettingListReq struct {
SMSProvider *string `json:"sms_provider,omitempty"`
MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"`
BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"`
DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"`
TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"`
AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"`
CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"`
DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"`
ReferralRewardAmount *float32 `json:"referral_reward_amount"`
CashbackPercentage *float32 `json:"cashback_percentage"`
DefaultMaxReferrals *int64 `json:"default_max_referrals"`
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidCurrency
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidCurrency
AmountForBetReferral ValidCurrency
CashbackAmountCap ValidCurrency
DefaultWinningLimit ValidInt64
ReferralRewardAmount ValidCurrency
CashbackPercentage ValidFloat32
DefaultMaxReferrals ValidInt64
}
func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
return ValidSettingList{
SMSProvider: ConvertStringPtr(settings.SMSProvider),
MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes),
BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit),
DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP),
TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit),
AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral),
CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap),
DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit),
ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount),
CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage),
DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals),
}
}
// Always make sure to run the validation before converting this
func (vsl *ValidSettingList) ToSettingList() SettingList {
return SettingList{
SMSProvider: SMSProvider(vsl.SMSProvider.Value),
MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value,
BetAmountLimit: vsl.BetAmountLimit.Value,
DailyTicketPerIP: vsl.DailyTicketPerIP.Value,
TotalWinningLimit: vsl.TotalWinningLimit.Value,
AmountForBetReferral: vsl.AmountForBetReferral.Value,
CashbackAmountCap: vsl.CashbackAmountCap.Value,
DefaultWinningLimit: vsl.DefaultWinningLimit.Value,
ReferralRewardAmount: vsl.ReferralRewardAmount.Value,
CashbackPercentage: vsl.CashbackPercentage.Value,
DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value,
}
}
// Custom Validations for non-generic types
func (vsl *ValidSettingList) CustomValidationSettings() error {
if !SMSProvider(vsl.SMSProvider.Value).IsValid() {
return fmt.Errorf("sms provider invalid")
}
return nil
}
func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 {
return map[string]*ValidInt64{
"max_number_of_outcomes": &vsl.MaxNumberOfOutcomes,
"daily_ticket_limit": &vsl.DailyTicketPerIP,
"default_winning_limit": &vsl.DefaultWinningLimit,
"default_max_referrals": &vsl.DefaultMaxReferrals,
}
}
func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency {
return map[string]*ValidCurrency{
"bet_amount_limit": &vsl.BetAmountLimit,
"total_winnings_limit": &vsl.TotalWinningLimit,
"amount_for_bet_referral": &vsl.AmountForBetReferral,
"cashback_amount_cap": &vsl.CashbackAmountCap,
"referral_reward_amount": &vsl.ReferralRewardAmount,
}
}
func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString {
return map[string]*ValidString{
"sms_provider": &vsl.SMSProvider,
}
}
func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool {
return map[string]*ValidBool{}
}
func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 {
return map[string]*ValidFloat32{
"cashback_percentage": &vsl.CashbackPercentage,
}
}
func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime {
return map[string]*ValidTime{}
}
// Setting Functions
func (vsl *ValidSettingList) GetTotalSettings() int {
return len(vsl.GetInt64SettingsMap()) +
len(vsl.GetCurrencySettingsMap()) +
len(vsl.GetStringSettingsMap()) +
len(vsl.GetBoolSettingsMap()) +
len(vsl.GetFloat32SettingsMap()) +
len(vsl.GetTimeSettingsMap())
}
func (vsl *ValidSettingList) GetAllValid() map[string]*bool {
settingValid := make(map[string]*bool)
for key, setting := range vsl.GetInt64SettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetCurrencySettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetStringSettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetBoolSettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetFloat32SettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetTimeSettingsMap() {
settingValid[key] = &(*setting).Valid
}
return settingValid
}
// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error {
// for key, setting := range settings {
// if key == searchKey {
// s, err := setVal(searchVal)
// if err != nil {
// return err
// }
// *setting = s
// }
// return nil
// }
// return ErrSettingNotFound
// }
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetInt64SettingsMap() {
if key == searchKey {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
*setting = ValidInt64{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetCurrencySettingsMap() {
if key == searchKey {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
*setting = ValidCurrency{Value: Currency(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetStringSettingsMap() {
if key == searchKey {
*setting = ValidString{Value: searchVal, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetBoolSettingsMap() {
if key == searchKey {
value, err := strconv.ParseBool(searchVal)
if err != nil {
return err
}
*setting = ValidBool{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetFloat32SettingsMap() {
if key == searchKey {
value, err := strconv.ParseFloat(searchVal, 32)
if err != nil {
return err
}
*setting = ValidFloat32{Value: float32(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetTimeSettingsMap() {
if key == searchKey {
value, err := time.Parse(time.RFC3339, searchVal)
if err != nil {
return err
}
*setting = ValidTime{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error {
setters := []func(string, string) error{
vsl.SetInt64Setting,
vsl.SetCurrencySetting,
vsl.SetStringSetting,
vsl.SetBoolSetting,
vsl.SetFloat32Setting,
vsl.SetTimeSetting,
}
for _, setter := range setters {
if err := setter(searchKey, searchVal); err != nil {
if err == ErrSettingNotFound {
// fmt.Printf("setting is not found %v \n", searchKey)
continue // not this setter, try the next
}
return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err)
}
return nil // successfully set
}
// If we get here, none of the setters matched
return ErrSettingNotFound
}
func convertValidSettings[T any](
settings map[string]*T,
isValid func(*T) bool,
toString func(*T) string,
) []Setting {
result := make([]Setting, 0, len(settings))
for key, s := range settings {
if isValid(s) {
result = append(result, Setting{
Key: key,
Value: toString(s),
})
}
}
return result
}
func (vsl *ValidSettingList) ConvertInt64Settings() []Setting {
return convertValidSettings(
vsl.GetInt64SettingsMap(),
func(s *ValidInt64) bool { return s.Valid },
func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) },
)
}
func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting {
return convertValidSettings(
vsl.GetCurrencySettingsMap(),
func(s *ValidCurrency) bool { return s.Valid },
func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) },
)
}
func (vsl *ValidSettingList) ConvertStringSettings() []Setting {
return convertValidSettings(
vsl.GetStringSettingsMap(),
func(s *ValidString) bool { return s.Valid },
func(s *ValidString) string { return s.Value },
)
}
func (vsl *ValidSettingList) ConvertBoolSettings() []Setting {
return convertValidSettings(
vsl.GetBoolSettingsMap(),
func(s *ValidBool) bool { return s.Valid },
func(s *ValidBool) string { return strconv.FormatBool(s.Value) },
)
}
func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting {
return convertValidSettings(
vsl.GetFloat32SettingsMap(),
func(s *ValidFloat32) bool { return s.Valid },
func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) },
)
}
func (vsl *ValidSettingList) ConvertTimeSettings() []Setting {
return convertValidSettings(
vsl.GetTimeSettingsMap(),
func(s *ValidTime) bool { return s.Valid },
func(s *ValidTime) string { return s.Value.Format(time.RFC3339) },
)
}
func validateSettings[T any](
settings map[string]*T,
customValidator func(*T) bool,
) error {
var errs []string
for key, s := range settings {
if !customValidator(s) {
errs = append(errs, fmt.Sprintf("%v is invalid", key))
}
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "; "))
}
return nil
}
func (vsl *ValidSettingList) ValidateInt64Settings() error {
return validateSettings(vsl.GetInt64SettingsMap(),
func(s *ValidInt64) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateCurrencySettings() error {
return validateSettings(vsl.GetCurrencySettingsMap(),
func(s *ValidCurrency) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateStringSettings() error {
return validateSettings(vsl.GetStringSettingsMap(),
func(s *ValidString) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateBoolSettings() error {
return validateSettings(vsl.GetBoolSettingsMap(),
func(s *ValidBool) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateFloat32Settings() error {
return validateSettings(vsl.GetFloat32SettingsMap(),
func(s *ValidFloat32) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateTimeSettings() error {
return validateSettings(vsl.GetTimeSettingsMap(),
func(s *ValidTime) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateAllSettings() error {
var errs []string
validators := []func() error{
vsl.ValidateInt64Settings,
vsl.ValidateCurrencySettings,
vsl.ValidateStringSettings,
vsl.ValidateBoolSettings,
vsl.ValidateFloat32Settings,
vsl.ValidateTimeSettings,
vsl.CustomValidationSettings,
}
for _, validator := range validators {
if err := validator(); err != nil {
errs = append(errs, err.Error())
}
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "; "))
}
return nil
}
func (vsl *ValidSettingList) ConvertAllSettings() []Setting {
totalCap := vsl.GetTotalSettings()
all := make([]Setting, 0, totalCap)
all = append(all, vsl.ConvertInt64Settings()...)
all = append(all, vsl.ConvertCurrencySettings()...)
all = append(all, vsl.ConvertStringSettings()...)
all = append(all, vsl.ConvertBoolSettings()...)
all = append(all, vsl.ConvertFloat32Settings()...)
all = append(all, vsl.ConvertTimeSettings()...)
return all
}
func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) {
var dbSettingList ValidSettingList
for _, setting := range settings {
if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err))
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList))
return SettingList{}, err
}
settingList := dbSettingList.ToSettingList()
return settingList, nil
}
func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) {
var dbSettingList ValidSettingList
for _, setting := range settings {
if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
MongoDBLogger.Warn("setting validation error", zap.Error(err))
return SettingList{}, err
}
settingList := dbSettingList.ToSettingList()
return settingList, nil
}