Yimaru-BackEnd/internal/domain/setting_list.go

618 lines
20 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"`
MinimumBetAmount Currency `json:"minimum_bet_amount"`
BetDuplicateLimit int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap Currency `json:"welcome_bonus_cap"`
WelcomeBonusCount int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"`
}
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"`
MinimumBetAmount float32 `json:"minimum_bet_amount"`
BetDuplicateLimit int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap float32 `json:"welcome_bonus_cap"`
WelcomeBonusCount int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"`
}
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,
MinimumBetAmount: settings.MinimumBetAmount.Float32(),
BetDuplicateLimit: settings.BetDuplicateLimit,
SendEmailOnBetFinish: settings.SendEmailOnBetFinish,
SendSMSOnBetFinish: settings.SendSMSOnBetFinish,
WelcomeBonusActive: settings.WelcomeBonusActive,
WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier,
WelcomeBonusCap: settings.WelcomeBonusCap.Float32(),
WelcomeBonusCount: settings.WelcomeBonusCount,
WelcomeBonusExpire: settings.WelcomeBonusExpire,
}
}
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"`
MinimumBetAmount *float32 `json:"minimum_bet_amount"`
BetDuplicateLimit *int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive *bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap *float32 `json:"welcome_bonus_cap"`
WelcomeBonusCount *int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry"`
}
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
MinimumBetAmount ValidCurrency
BetDuplicateLimit ValidInt64
SendEmailOnBetFinish ValidBool
SendSMSOnBetFinish ValidBool
WelcomeBonusActive ValidBool
WelcomeBonusMultiplier ValidFloat32
WelcomeBonusCap ValidCurrency
WelcomeBonusCount ValidInt64
WelcomeBonusExpire 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),
MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount),
BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit),
SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish),
SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish),
WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive),
WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier),
WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap),
WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount),
WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire),
}
}
// 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,
MinimumBetAmount: vsl.MinimumBetAmount.Value,
BetDuplicateLimit: vsl.BetDuplicateLimit.Value,
SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value,
SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value,
WelcomeBonusActive: vsl.WelcomeBonusActive.Value,
WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value,
WelcomeBonusCap: vsl.WelcomeBonusCap.Value,
WelcomeBonusCount: vsl.WelcomeBonusCount.Value,
WelcomeBonusExpire: vsl.WelcomeBonusExpire.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,
"bet_duplicate_limit": &vsl.BetDuplicateLimit,
"welcome_bonus_count": &vsl.WelcomeBonusCount,
"welcome_bonus_expiry": &vsl.WelcomeBonusExpire,
}
}
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,
"minimum_bet_amount": &vsl.MinimumBetAmount,
"welcome_bonus_cap": &vsl.WelcomeBonusCap,
}
}
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{
"send_email_on_bet_finish": &vsl.SendEmailOnBetFinish,
"send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish,
"welcome_bonus_active": &vsl.WelcomeBonusActive,
}
}
func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 {
return map[string]*ValidFloat32{
"cashback_percentage": &vsl.CashbackPercentage,
"welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier,
}
}
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
}