diff --git a/engine/calldesc.go b/engine/calldesc.go index a5a403b97..c1c314cf0 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -45,6 +45,10 @@ func init() { var err error switch DB { case "map": + if cgrCfg := config.CgrConfig(); cgrCfg == nil { + cgrCfg, _ = config.NewDefaultCGRConfig() + config.SetCgrConfig(cgrCfg) + } ratingStorage, _ = NewMapStorage() accountingStorage, _ = NewMapStorage() case utils.MONGO: diff --git a/engine/storage_map.go b/engine/storage_map.go index 5a8cb3769..2ddf3f775 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -21,6 +21,7 @@ import ( "bytes" "compress/zlib" "errors" + "fmt" "io/ioutil" "strings" "sync" @@ -70,11 +71,13 @@ func (s storage) smembers(key string, ms Marshaler) (idMap utils.StringMap, ok b } func NewMapStorage() (*MapStorage, error) { - return &MapStorage{dict: make(map[string][]byte), ms: NewCodecMsgpackMarshaler(), cacheCfg: &config.CacheConfig{RatingPlans: &config.CacheParamConfig{Precache: true}}}, nil + return &MapStorage{dict: make(map[string][]byte), ms: NewCodecMsgpackMarshaler(), cacheCfg: config.CgrConfig().CacheConfig}, nil } -func NewMapStorageJson() (*MapStorage, error) { - return &MapStorage{dict: make(map[string][]byte), ms: new(JSONBufMarshaler), cacheCfg: &config.CacheConfig{RatingPlans: &config.CacheParamConfig{Precache: true}}}, nil +func NewMapStorageJson() (mpStorage *MapStorage, err error) { + mpStorage, err = NewMapStorage() + mpStorage.ms = new(JSONBufMarshaler) + return } func (ms *MapStorage) Close() {} @@ -95,7 +98,7 @@ func (ms *MapStorage) SelectDatabase(dbName string) (err error) { } func (ms *MapStorage) RebuildReverseForPrefix(prefix string) error { - // FIXME: should do transaction + // ToDo: should do transaction keys, err := ms.GetKeysForPrefix(prefix) if err != nil { return err @@ -142,7 +145,6 @@ func (ms *MapStorage) RebuildReverseForPrefix(prefix string) error { return nil } -// FixMe func (ms *MapStorage) LoadRatingCache(dstIDs, rvDstIDs, rplIDs, rpfIDs, actIDs, aplIDs, aapIDs, atrgIDs, sgIDs, lcrIDs, dcIDs []string) error { if ms.cacheCfg == nil { return nil @@ -250,6 +252,119 @@ func (ms *MapStorage) PreloadCacheForPrefix(prefix string) error { // CacheDataFromDB loads data to cache, // prefix represents the cache prefix, IDs should be nil if all available data should be loaded func (ms *MapStorage) CacheDataFromDB(prefix string, IDs []string, mustBeCached bool) (err error) { + if !utils.IsSliceMember([]string{utils.DESTINATION_PREFIX, + utils.REVERSE_DESTINATION_PREFIX, + utils.RATING_PLAN_PREFIX, + utils.RATING_PROFILE_PREFIX, + utils.ACTION_PREFIX, + utils.ACTION_PLAN_PREFIX, + utils.AccountActionPlansPrefix, + utils.ACTION_TRIGGER_PREFIX, + utils.SHARED_GROUP_PREFIX, + utils.DERIVEDCHARGERS_PREFIX, + utils.LCR_PREFIX, + utils.ALIASES_PREFIX, + utils.REVERSE_ALIASES_PREFIX, + utils.ResourceLimitsPrefix}, prefix) { + return utils.NewCGRError(utils.REDIS, + utils.MandatoryIEMissingCaps, + utils.UnsupportedCachePrefix, + fmt.Sprintf("prefix <%s> is not a supported cache prefix", prefix)) + } + if IDs == nil { + keyIDs, err := ms.GetKeysForPrefix(prefix) + if err != nil { + return utils.NewCGRError(utils.REDIS, + utils.ServerErrorCaps, + err.Error(), + fmt.Sprintf("MapStorage error <%s> querying keys for prefix: <%s>", prefix)) + } + for _, keyID := range keyIDs { + if mustBeCached { // Only consider loading ids which are already in cache + if _, hasIt := cache.Get(keyID); !hasIt { + continue + } + } + IDs = append(IDs, keyID[len(prefix):]) + } + var nrItems int + switch prefix { + case utils.DESTINATION_PREFIX: + nrItems = ms.cacheCfg.Destinations.Limit + case utils.REVERSE_DESTINATION_PREFIX: + nrItems = ms.cacheCfg.ReverseDestinations.Limit + case utils.RATING_PLAN_PREFIX: + nrItems = ms.cacheCfg.RatingPlans.Limit + case utils.RATING_PROFILE_PREFIX: + nrItems = ms.cacheCfg.RatingProfiles.Limit + case utils.ACTION_PREFIX: + nrItems = ms.cacheCfg.Actions.Limit + case utils.ACTION_PLAN_PREFIX: + nrItems = ms.cacheCfg.ActionPlans.Limit + case utils.AccountActionPlansPrefix: + nrItems = ms.cacheCfg.AccountActionPlans.Limit + case utils.ACTION_TRIGGER_PREFIX: + nrItems = ms.cacheCfg.ActionTriggers.Limit + case utils.SHARED_GROUP_PREFIX: + nrItems = ms.cacheCfg.SharedGroups.Limit + case utils.DERIVEDCHARGERS_PREFIX: + nrItems = ms.cacheCfg.DerivedChargers.Limit + case utils.LCR_PREFIX: + nrItems = ms.cacheCfg.Lcr.Limit + case utils.ALIASES_PREFIX: + nrItems = ms.cacheCfg.Aliases.Limit + case utils.REVERSE_ALIASES_PREFIX: + nrItems = ms.cacheCfg.ReverseAliases.Limit + case utils.ResourceLimitsPrefix: + nrItems = ms.cacheCfg.ResourceLimits.Limit + } + if nrItems != 0 && nrItems < len(IDs) { + IDs = IDs[:nrItems] + } + } + for _, dataID := range IDs { + if mustBeCached { + if _, hasIt := cache.Get(prefix + dataID); !hasIt { // only cache if previously there + continue + } + } + switch prefix { + case utils.DESTINATION_PREFIX: + _, err = ms.GetDestination(dataID, true, utils.NonTransactional) + case utils.REVERSE_DESTINATION_PREFIX: + _, err = ms.GetReverseDestination(dataID, true, utils.NonTransactional) + case utils.RATING_PLAN_PREFIX: + _, err = ms.GetRatingPlan(dataID, true, utils.NonTransactional) + case utils.RATING_PROFILE_PREFIX: + _, err = ms.GetRatingProfile(dataID, true, utils.NonTransactional) + case utils.ACTION_PREFIX: + _, err = ms.GetActions(dataID, true, utils.NonTransactional) + case utils.ACTION_PLAN_PREFIX: + _, err = ms.GetActionPlan(dataID, true, utils.NonTransactional) + case utils.AccountActionPlansPrefix: + _, err = ms.GetAccountActionPlans(dataID, true, utils.NonTransactional) + case utils.ACTION_TRIGGER_PREFIX: + _, err = ms.GetActionTriggers(dataID, true, utils.NonTransactional) + case utils.SHARED_GROUP_PREFIX: + _, err = ms.GetSharedGroup(dataID, true, utils.NonTransactional) + case utils.DERIVEDCHARGERS_PREFIX: + _, err = ms.GetDerivedChargers(dataID, true, utils.NonTransactional) + case utils.LCR_PREFIX: + _, err = ms.GetLCR(dataID, true, utils.NonTransactional) + case utils.ALIASES_PREFIX: + _, err = ms.GetAlias(dataID, true, utils.NonTransactional) + case utils.REVERSE_ALIASES_PREFIX: + _, err = ms.GetReverseAlias(dataID, true, utils.NonTransactional) + case utils.ResourceLimitsPrefix: + _, err = ms.GetResourceLimit(dataID, true, utils.NonTransactional) + } + if err != nil { + return utils.NewCGRError(utils.REDIS, + utils.ServerErrorCaps, + err.Error(), + fmt.Sprintf("error <%s> querying MapStorage for category: <%s>, dataID: <%s>", prefix, dataID)) + } + } return } @@ -343,7 +458,9 @@ func (ms *MapStorage) GetRatingProfile(key string, skipCache bool, transactionID cCommit := cacheCommit(transactionID) if values, ok := ms.dict[key]; ok { rpf = new(RatingProfile) - err = ms.ms.Unmarshal(values, rpf) + if err = ms.ms.Unmarshal(values, &rpf); err != nil { + return nil, err + } } else { cache.Set(key, nil, cCommit, transactionID) return nil, utils.ErrNotFound @@ -439,14 +556,18 @@ func (ms *MapStorage) GetDestination(key string, skipCache bool, transactionID s } r.Close() dest = new(Destination) - err = ms.ms.Unmarshal(out, dest) + err = ms.ms.Unmarshal(out, &dest) if err != nil { - cache.Set(key, dest, cCommit, transactionID) + cache.Set(key, nil, cCommit, transactionID) + return nil, utils.ErrNotFound } - } else { + } + if dest == nil { cache.Set(key, nil, cCommit, transactionID) return nil, utils.ErrNotFound } + cache.Set(key, dest, cCommit, transactionID) + return } @@ -508,16 +629,19 @@ func (ms *MapStorage) RemoveDestination(destID string, transactionID string) (er if err != nil { return } + ms.mu.Lock() delete(ms.dict, key) ms.mu.Unlock() cache.RemKey(key, cacheCommit(transactionID), transactionID) + for _, prefix := range d.Prefixes { ms.mu.Lock() ms.dict.srem(utils.REVERSE_DESTINATION_PREFIX+prefix, destID, ms.ms) ms.mu.Unlock() ms.GetReverseDestination(prefix, true, transactionID) // it will recache the destination } + return } @@ -577,9 +701,9 @@ func (ms *MapStorage) GetActions(key string, skipCache bool, transactionID strin ms.mu.RLock() defer ms.mu.RUnlock() cCommit := cacheCommit(transactionID) - key = utils.ACTION_PREFIX + key + cachekey := utils.ACTION_PREFIX + key if !skipCache { - if x, err := cache.GetCloned(key); err != nil { + if x, err := cache.GetCloned(cachekey); err != nil { if err.Error() != utils.ItemNotFound { return nil, err } @@ -589,13 +713,13 @@ func (ms *MapStorage) GetActions(key string, skipCache bool, transactionID strin return x.(Actions), nil } } - if values, ok := ms.dict[key]; ok { + if values, ok := ms.dict[cachekey]; ok { err = ms.ms.Unmarshal(values, &as) } else { - cache.Set(key, nil, cCommit, transactionID) + cache.Set(cachekey, nil, cCommit, transactionID) return nil, utils.ErrNotFound } - cache.Set(key, as, cCommit, transactionID) + cache.Set(cachekey, as, cCommit, transactionID) return } @@ -603,26 +727,28 @@ func (ms *MapStorage) SetActions(key string, as Actions, transactionID string) ( ms.mu.Lock() defer ms.mu.Unlock() cCommit := cacheCommit(transactionID) + cachekey := utils.ACTION_PREFIX + key result, err := ms.ms.Marshal(&as) - ms.dict[utils.ACTION_PREFIX+key] = result - cache.RemKey(utils.ACTION_PREFIX+key, cCommit, transactionID) + ms.dict[cachekey] = result + cache.RemKey(cachekey, cCommit, transactionID) return } func (ms *MapStorage) RemoveActions(key string, transactionID string) (err error) { + cachekey := utils.ACTION_PREFIX + key ms.mu.Lock() - defer ms.mu.Unlock() - delete(ms.dict, utils.ACTION_PREFIX+key) - cache.RemKey(utils.ACTION_PREFIX+key, cacheCommit(transactionID), transactionID) + delete(ms.dict, cachekey) + ms.mu.Unlock() + cache.RemKey(cachekey, cacheCommit(transactionID), transactionID) return } func (ms *MapStorage) GetSharedGroup(key string, skipCache bool, transactionID string) (sg *SharedGroup, err error) { ms.mu.RLock() defer ms.mu.RUnlock() - key = utils.SHARED_GROUP_PREFIX + key + cachekey := utils.SHARED_GROUP_PREFIX + key if !skipCache { - if x, ok := cache.Get(key); ok { + if x, ok := cache.Get(cachekey); ok { if x != nil { return x.(*SharedGroup), nil } @@ -630,13 +756,13 @@ func (ms *MapStorage) GetSharedGroup(key string, skipCache bool, transactionID s } } cCommit := cacheCommit(transactionID) - if values, ok := ms.dict[key]; ok { + if values, ok := ms.dict[cachekey]; ok { err = ms.ms.Unmarshal(values, &sg) if err == nil { - cache.Set(key, sg, cCommit, transactionID) + cache.Set(cachekey, sg, cCommit, transactionID) } } else { - cache.Set(key, nil, cCommit, transactionID) + cache.Set(cachekey, nil, cCommit, transactionID) return nil, utils.ErrNotFound } return @@ -654,12 +780,19 @@ func (ms *MapStorage) SetSharedGroup(sg *SharedGroup, transactionID string) (err func (ms *MapStorage) GetAccount(key string) (ub *Account, err error) { ms.mu.RLock() defer ms.mu.RUnlock() - if values, ok := ms.dict[utils.ACCOUNT_PREFIX+key]; ok { - ub = &Account{ID: key} - err = ms.ms.Unmarshal(values, ub) - } else { + values, ok := ms.dict[utils.ACCOUNT_PREFIX+key] + if !ok { return nil, utils.ErrNotFound } + ub = &Account{ID: key} + err = ms.ms.Unmarshal(values, &ub) + if err != nil { + return nil, err + } + if len(values) == 0 { + return nil, utils.ErrNotFound + } + return } @@ -785,41 +918,40 @@ func (ms *MapStorage) RemoveUser(key string) error { func (ms *MapStorage) GetAlias(key string, skipCache bool, transactionID string) (al *Alias, err error) { ms.mu.RLock() defer ms.mu.RUnlock() - origKey := key - key = utils.ALIASES_PREFIX + key + cacheKey := utils.ALIASES_PREFIX + key + cCommit := cacheCommit(transactionID) if !skipCache { - if x, ok := cache.Get(key); ok { - if x != nil { - al = &Alias{Values: x.(AliasValues)} - al.SetId(origKey) - return al, nil + if x, ok := cache.Get(cacheKey); ok { + if x == nil { + return nil, utils.ErrNotFound } - return nil, utils.ErrNotFound + return x.(*Alias), nil } } - cCommit := cacheCommit(transactionID) - if values, ok := ms.dict[key]; ok { + if values, ok := ms.dict[cacheKey]; ok { al = &Alias{Values: make(AliasValues, 0)} - al.SetId(key[len(utils.ALIASES_PREFIX):]) - err = ms.ms.Unmarshal(values, &al.Values) - if err == nil { - cache.Set(key, al.Values, cCommit, transactionID) + al.SetId(key) + if err = ms.ms.Unmarshal(values, &al.Values); err != nil { + return nil, err } } else { - cache.Set(key, nil, cCommit, transactionID) + cache.Set(cacheKey, nil, cCommit, transactionID) return nil, utils.ErrNotFound } - return al, nil + cache.Set(cacheKey, al, cCommit, transactionID) + return + } func (ms *MapStorage) SetAlias(al *Alias, transactionID string) error { - ms.mu.Lock() - defer ms.mu.Unlock() + result, err := ms.ms.Marshal(al.Values) if err != nil { return err } key := utils.ALIASES_PREFIX + al.GetId() + ms.mu.Lock() + defer ms.mu.Unlock() ms.dict[key] = result cache.RemKey(key, cacheCommit(transactionID), transactionID) return nil @@ -837,15 +969,15 @@ func (ms *MapStorage) GetReverseAlias(reverseID string, skipCache bool, transact return nil, utils.ErrNotFound } } - var values []string + cCommit := cacheCommit(transactionID) if idMap, ok := ms.dict.smembers(key, ms.ms); len(idMap) > 0 && ok { - values = idMap.Slice() + ids = idMap.Slice() } else { cache.Set(key, nil, cCommit, transactionID) return nil, utils.ErrNotFound } - cache.Set(key, values, cCommit, transactionID) + cache.Set(key, ids, cCommit, transactionID) return } @@ -1027,6 +1159,8 @@ func (ms *MapStorage) GetAllActionPlans() (ats map[string]*ActionPlan, err error } func (ms *MapStorage) GetAccountActionPlans(acntID string, skipCache bool, transactionID string) (apIDs []string, err error) { + ms.mu.RLock() + defer ms.mu.RUnlock() key := utils.AccountActionPlansPrefix + acntID if !skipCache { if x, ok := cache.Get(key); ok { @@ -1036,9 +1170,7 @@ func (ms *MapStorage) GetAccountActionPlans(acntID string, skipCache bool, trans return x.([]string), nil } } - ms.mu.RLock() values, ok := ms.dict[key] - ms.mu.RUnlock() if !ok { cache.Set(key, nil, cacheCommit(transactionID), transactionID) err = utils.ErrNotFound @@ -1053,8 +1185,7 @@ func (ms *MapStorage) GetAccountActionPlans(acntID string, skipCache bool, trans func (ms *MapStorage) SetAccountActionPlans(acntID string, apIDs []string, overwrite bool) (err error) { if !overwrite { - oldaPlIDs, err := ms.GetAccountActionPlans(acntID, true, utils.NonTransactional) - if err != nil && err != utils.ErrNotFound { + if oldaPlIDs, err := ms.GetAccountActionPlans(acntID, true, utils.NonTransactional); err != nil && err != utils.ErrNotFound { return err } else { for _, oldAPid := range oldaPlIDs { @@ -1064,7 +1195,6 @@ func (ms *MapStorage) SetAccountActionPlans(acntID string, apIDs []string, overw } } } - ms.mu.Lock() defer ms.mu.Unlock() result, err := ms.ms.Marshal(apIDs) @@ -1072,12 +1202,11 @@ func (ms *MapStorage) SetAccountActionPlans(acntID string, apIDs []string, overw return err } ms.dict[utils.AccountActionPlansPrefix+acntID] = result + return } func (ms *MapStorage) RemAccountActionPlans(acntID string, apIDs []string) (err error) { - ms.mu.Lock() - defer ms.mu.Unlock() key := utils.AccountActionPlansPrefix + acntID if len(apIDs) == 0 { delete(ms.dict, key) @@ -1094,10 +1223,17 @@ func (ms *MapStorage) RemAccountActionPlans(acntID string, apIDs []string) (err } i++ } + ms.mu.Lock() + defer ms.mu.Unlock() if len(oldaPlIDs) == 0 { delete(ms.dict, key) return } + var result []byte + if result, err = ms.ms.Marshal(oldaPlIDs); err != nil { + return err + } + ms.dict[key] = result return } @@ -1277,7 +1413,8 @@ func (ms *MapStorage) SetResourceLimit(rl *ResourceLimit, transactionID string) if err != nil { return err } - ms.dict[utils.ResourceLimitsPrefix+rl.ID] = result + key := utils.ResourceLimitsPrefix + rl.ID + ms.dict[key] = result return nil } @@ -1314,9 +1451,10 @@ func (ms *MapStorage) SetReqFilterIndexes(dbKey string, indexes map[string]map[s return } func (ms *MapStorage) MatchReqFilterIndex(dbKey, fieldValKey string) (itemIDs utils.StringMap, err error) { + cacheKey := dbKey + fieldValKey ms.mu.RLock() defer ms.mu.RUnlock() - if x, ok := cache.Get(dbKey + fieldValKey); ok { // Attempt to find in cache first + if x, ok := cache.Get(cacheKey); ok { // Attempt to find in cache first if x != nil { return x.(utils.StringMap), nil } @@ -1325,7 +1463,7 @@ func (ms *MapStorage) MatchReqFilterIndex(dbKey, fieldValKey string) (itemIDs ut // Not found in cache, check in DB values, ok := ms.dict[dbKey] if !ok { - cache.Set(dbKey+fieldValKey, nil, true, utils.NonTransactional) + cache.Set(cacheKey, nil, true, utils.NonTransactional) return nil, utils.ErrNotFound } var indexes map[string]map[string]utils.StringMap @@ -1336,7 +1474,12 @@ func (ms *MapStorage) MatchReqFilterIndex(dbKey, fieldValKey string) (itemIDs ut if _, hasIt := indexes[keySplt[0]]; hasIt { itemIDs = indexes[keySplt[0]][keySplt[1]] } - cache.Set(dbKey+fieldValKey, itemIDs, true, utils.NonTransactional) + //Verify items + if len(itemIDs) == 0 { + cache.Set(cacheKey, nil, true, utils.NonTransactional) + return nil, utils.ErrNotFound + } + cache.Set(cacheKey, itemIDs, true, utils.NonTransactional) return } diff --git a/migrator/accounts.go b/migrator/accounts.go index c4c6c6de1..9bcb46d19 100644 --- a/migrator/accounts.go +++ b/migrator/accounts.go @@ -18,6 +18,9 @@ along with this program. If not, see package migrator import ( + "fmt" + "log" + "strings" "time" "github.com/cgrates/cgrates/engine" @@ -53,6 +56,12 @@ type v1Balance struct { TimingIDs string Disabled bool } +type v1UnitsCounter struct { + Direction string + BalanceType string + // Units float64 + Balances v1BalanceChain // first balance is the general one (no destination) +} func (b *v1Balance) IsDefault() bool { return (b.DestinationIds == "" || b.DestinationIds == utils.ANY) && @@ -64,34 +73,149 @@ func (b *v1Balance) IsDefault() bool { b.Disabled == false } -type v1UnitsCounter struct { - Direction string - BalanceType string - // Units float64 - Balances v1BalanceChain // first balance is the general one (no destination) -} - -type v1ActionTriggers []*v1ActionTrigger - -type v1ActionTrigger struct { - Id string - ThresholdType string - ThresholdValue float64 - Recurrent bool - MinSleep time.Duration - BalanceId string - BalanceType string - BalanceDirection string - BalanceDestinationIds string - BalanceWeight float64 - BalanceExpirationDate time.Time - BalanceTimingTags string - BalanceRatingSubject string - BalanceCategory string - BalanceSharedGroup string - BalanceDisabled bool - Weight float64 - ActionsId string - MinQueuedItems int - Executed bool +func (v1Acc v1Account) AsAccount() (ac engine.Account) { + // transfer data into new structure + ac = engine.Account{ + ID: v1Acc.Id, + BalanceMap: make(map[string]engine.Balances, len(v1Acc.BalanceMap)), + UnitCounters: make(engine.UnitCounters, len(v1Acc.UnitCounters)), + ActionTriggers: make(engine.ActionTriggers, len(v1Acc.ActionTriggers)), + AllowNegative: v1Acc.AllowNegative, + Disabled: v1Acc.Disabled, + } + idElements := strings.Split(ac.ID, utils.CONCATENATED_KEY_SEP) + if len(idElements) != 3 { + log.Printf("Malformed account ID %s", v1Acc.Id) + } + ac.ID = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) + // balances + for oldBalKey, oldBalChain := range v1Acc.BalanceMap { + keyElements := strings.Split(oldBalKey, "*") + newBalKey := "*" + keyElements[1] + newBalDirection := "*" + idElements[0] + ac.BalanceMap[newBalKey] = make(engine.Balances, len(oldBalChain)) + for index, oldBal := range oldBalChain { + // check default to set new id + ac.BalanceMap[newBalKey][index] = &engine.Balance{ + Uuid: oldBal.Uuid, + ID: oldBal.Id, + Value: oldBal.Value, + Directions: utils.ParseStringMap(newBalDirection), + ExpirationDate: oldBal.ExpirationDate, + Weight: oldBal.Weight, + DestinationIDs: utils.ParseStringMap(oldBal.DestinationIds), + RatingSubject: oldBal.RatingSubject, + Categories: utils.ParseStringMap(oldBal.Category), + SharedGroups: utils.ParseStringMap(oldBal.SharedGroup), + Timings: oldBal.Timings, + TimingIDs: utils.ParseStringMap(oldBal.TimingIDs), + Disabled: oldBal.Disabled, + } + } + } + // unit counters + for _, oldUc := range v1Acc.UnitCounters { + newUc := &engine.UnitCounter{Counters: make(engine.CounterFilters, len(oldUc.Balances))} + for index, oldUcBal := range oldUc.Balances { + bf := &engine.BalanceFilter{} + if oldUcBal.Uuid != "" { + bf.Uuid = utils.StringPointer(oldUcBal.Uuid) + } + if oldUcBal.Id != "" { + bf.ID = utils.StringPointer(oldUcBal.Id) + } + if oldUc.BalanceType != "" { + bf.Type = utils.StringPointer(oldUc.BalanceType) + } + if oldUc.Direction != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldUc.Direction)) + } + if !oldUcBal.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldUcBal.ExpirationDate) + } + if oldUcBal.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldUcBal.Weight) + } + if oldUcBal.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.DestinationIds)) + } + if oldUcBal.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldUcBal.RatingSubject) + } + if oldUcBal.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.Category)) + } + if oldUcBal.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.SharedGroup)) + } + if oldUcBal.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.TimingIDs)) + } + if oldUcBal.Disabled != false { + bf.Disabled = utils.BoolPointer(oldUcBal.Disabled) + } + bf.Timings = oldUcBal.Timings + cf := &engine.CounterFilter{ + Value: oldUcBal.Value, + Filter: bf, + } + newUc.Counters[index] = cf + } + ac.UnitCounters[oldUc.BalanceType] = append(ac.UnitCounters[oldUc.BalanceType], newUc) + } + //action triggers + for index, oldAtr := range v1Acc.ActionTriggers { + at := &engine.ActionTrigger{ + UniqueID: oldAtr.Id, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsID: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, + } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if oldAtr.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) + } + if oldAtr.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) + } + if oldAtr.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) + } + if oldAtr.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) + } + if oldAtr.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + ac.ActionTriggers[index] = at + if ac.ActionTriggers[index].ThresholdType == "*min_counter" || + ac.ActionTriggers[index].ThresholdType == "*max_counter" { + ac.ActionTriggers[index].ThresholdType = strings.Replace(ac.ActionTriggers[index].ThresholdType, "_", "_event_", 1) + } + } + return } diff --git a/migrator/accounts_test.go b/migrator/accounts_test.go new file mode 100644 index 000000000..af782d3f6 --- /dev/null +++ b/migrator/accounts_test.go @@ -0,0 +1,41 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "reflect" + "testing" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func TestV1AccountAsAccount(t *testing.T) { + v1b := &v1Balance{Value: 10, Weight: 10, DestinationIds: "NAT"} + v1Acc := &v1Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]v1BalanceChain{utils.VOICE: v1BalanceChain{v1b}, utils.MONETARY: v1BalanceChain{&v1Balance{Value: 21}}}} + v2 := &engine.Balance{Uuid: "", ID: "", Value: 10, Directions: utils.StringMap{"*OUT": true}, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "", Categories: utils.NewStringMap(""), SharedGroups: utils.NewStringMap(""), TimingIDs: utils.NewStringMap("")} + m2 := &engine.Balance{Uuid: "", ID: "", Value: 21, Directions: utils.StringMap{"*OUT": true}, DestinationIDs: utils.NewStringMap(""), RatingSubject: "", Categories: utils.NewStringMap(""), SharedGroups: utils.NewStringMap(""), TimingIDs: utils.NewStringMap("")} + testAccount := &engine.Account{ID: "CUSTOMER_1:rif", BalanceMap: map[string]engine.Balances{utils.VOICE: engine.Balances{v2}, utils.MONETARY: engine.Balances{m2}}, UnitCounters: engine.UnitCounters{}, ActionTriggers: engine.ActionTriggers{}} + if def := v1b.IsDefault(); def != false { + t.Errorf("Expecting: false, received: true") + } + newAcc := v1Acc.AsAccount() + if !reflect.DeepEqual(*testAccount, newAcc) { + t.Errorf("Expecting: %+v, received: %+v", *testAccount, newAcc) + } +} diff --git a/migrator/action.go b/migrator/action.go new file mode 100644 index 000000000..e73e88efd --- /dev/null +++ b/migrator/action.go @@ -0,0 +1,86 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type v1Action struct { + Id string + ActionType string + BalanceType string + Direction string + ExtraParameters string + ExpirationString string + Weight float64 + Balance *v1Balance +} + +type v1Actions []*v1Action + +func (v1Act v1Action) AsAction() (act engine.Action) { + act = engine.Action{ + Id: v1Act.Id, + ActionType: v1Act.ActionType, + ExtraParameters: v1Act.ExtraParameters, + ExpirationString: v1Act.ExpirationString, + Weight: v1Act.Weight, + Balance: &engine.BalanceFilter{}, + } + bf := act.Balance + if v1Act.Balance.Uuid != "" { + bf.Uuid = utils.StringPointer(v1Act.Balance.Uuid) + } + if v1Act.Balance.Id != "" { + bf.ID = utils.StringPointer(v1Act.Balance.Id) + } + if v1Act.BalanceType != "" { + bf.Type = utils.StringPointer(v1Act.BalanceType) + } + if v1Act.Balance.Value != 0 { + bf.Value = &utils.ValueFormula{Static: v1Act.Balance.Value} + } + if v1Act.Balance.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(v1Act.Balance.RatingSubject) + } + if v1Act.Balance.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(v1Act.Balance.DestinationIds)) + } + if v1Act.Balance.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(v1Act.Balance.TimingIDs)) + } + if v1Act.Balance.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(v1Act.Balance.Category)) + } + if v1Act.Balance.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(v1Act.Balance.SharedGroup)) + } + if v1Act.Balance.Weight != 0 { + bf.Weight = utils.Float64Pointer(v1Act.Balance.Weight) + } + if v1Act.Balance.Disabled != false { + bf.Disabled = utils.BoolPointer(v1Act.Balance.Disabled) + } + if !v1Act.Balance.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(v1Act.Balance.ExpirationDate) + } + bf.Timings = v1Act.Balance.Timings + return +} diff --git a/migrator/action_plan.go b/migrator/action_plan.go new file mode 100644 index 000000000..0c4e56019 --- /dev/null +++ b/migrator/action_plan.go @@ -0,0 +1,75 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "fmt" + "strings" + "time" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type v1ActionPlan struct { + Uuid string // uniquely identify the timing + Id string // informative purpose only + AccountIds []string + Timing *engine.RateInterval + Weight float64 + ActionsId string + actions v1Actions + stCache time.Time // cached time of the next start +} + +type v1ActionPlans []*v1ActionPlan + +func (at *v1ActionPlan) IsASAP() bool { + if at.Timing == nil { + return false + } + return at.Timing.Timing.StartTime == utils.ASAP +} + +func (v1AP v1ActionPlan) AsActionPlan() (ap engine.ActionPlan) { + for idx, actionId := range v1AP.AccountIds { + idElements := strings.Split(actionId, utils.CONCATENATED_KEY_SEP) + if len(idElements) != 3 { + continue + } + v1AP.AccountIds[idx] = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) + } + ap = engine.ActionPlan{ + Id: v1AP.Id, + AccountIDs: make(utils.StringMap), + } + if x := v1AP.IsASAP(); !x { + for _, accID := range v1AP.AccountIds { + if _, exists := ap.AccountIDs[accID]; !exists { + ap.AccountIDs[accID] = true + } + } + } + ap.ActionTimings = append(ap.ActionTimings, &engine.ActionTiming{ + Uuid: utils.GenUUID(), + Timing: v1AP.Timing, + ActionsID: v1AP.ActionsId, + Weight: v1AP.Weight, + }) + return +} diff --git a/migrator/action_plan_test.go b/migrator/action_plan_test.go new file mode 100644 index 000000000..8b4135559 --- /dev/null +++ b/migrator/action_plan_test.go @@ -0,0 +1,39 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "reflect" + "testing" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func TestV1ActionPlanAsActionPlan(t *testing.T) { + v1ap := &v1ActionPlan{Id: "test", AccountIds: []string{"one"}, Timing: &engine.RateInterval{Timing: new(engine.RITiming)}} + ap := &engine.ActionPlan{Id: "test", AccountIDs: utils.StringMap{"one": true}, ActionTimings: []*engine.ActionTiming{&engine.ActionTiming{Timing: &engine.RateInterval{Timing: new(engine.RITiming)}}}} + newap := v1ap.AsActionPlan() + if ap.Id != newap.Id || !reflect.DeepEqual(ap.AccountIDs, newap.AccountIDs) { + t.Errorf("Expecting: %+v, received: %+v", *ap, newap) + } else if !reflect.DeepEqual(ap.ActionTimings[0].Timing, newap.ActionTimings[0].Timing) { + t.Errorf("Expecting: %+v, received: %+v", ap.ActionTimings[0].Timing, newap.ActionTimings[0].Timing) + } else if ap.ActionTimings[0].Weight != newap.ActionTimings[0].Weight || ap.ActionTimings[0].ActionsID != newap.ActionTimings[0].ActionsID { + t.Errorf("Expecting: %+v, received: %+v", ap.ActionTimings[0].Weight, newap.ActionTimings[0].Weight) + } +} diff --git a/migrator/action_test.go b/migrator/action_test.go new file mode 100644 index 000000000..db29d3db6 --- /dev/null +++ b/migrator/action_test.go @@ -0,0 +1,34 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "reflect" + "testing" + + "github.com/cgrates/cgrates/engine" +) + +func TestV1ActionsAsActions(t *testing.T) { + v1act := &v1Action{Id: "", ActionType: "", BalanceType: "", Direction: "INBOUND", ExtraParameters: "", ExpirationString: "", Balance: &v1Balance{}} + act := &engine.Action{Id: "", ActionType: "", ExtraParameters: "", ExpirationString: "", Weight: 0.00, Balance: &engine.BalanceFilter{}} + newact := v1act.AsAction() + if !reflect.DeepEqual(*act, newact) { + t.Errorf("Expecting: %+v, received: %+v", *act, newact) + } +} diff --git a/migrator/action_trigger.go b/migrator/action_trigger.go new file mode 100644 index 000000000..862115352 --- /dev/null +++ b/migrator/action_trigger.go @@ -0,0 +1,89 @@ +package migrator + +import ( + "strings" + "time" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type v1ActionTrigger struct { + Id string + ThresholdType string + ThresholdValue float64 + Recurrent bool + MinSleep time.Duration + BalanceId string + BalanceType string + BalanceDirection string + BalanceDestinationIds string + BalanceWeight float64 + BalanceExpirationDate time.Time + BalanceTimingTags string + BalanceRatingSubject string + BalanceCategory string + BalanceSharedGroup string + BalanceDisabled bool + Weight float64 + ActionsId string + MinQueuedItems int + Executed bool +} + +type v1ActionTriggers []*v1ActionTrigger + +func (v1Act v1ActionTrigger) AsActionTrigger() (at engine.ActionTrigger) { + + at = engine.ActionTrigger{ + UniqueID: v1Act.Id, + ThresholdType: v1Act.ThresholdType, + ThresholdValue: v1Act.ThresholdValue, + Recurrent: v1Act.Recurrent, + MinSleep: v1Act.MinSleep, + Weight: v1Act.Weight, + ActionsID: v1Act.ActionsId, + MinQueuedItems: v1Act.MinQueuedItems, + Executed: v1Act.Executed, + } + bf := &engine.BalanceFilter{} + if v1Act.BalanceId != "" { + bf.ID = utils.StringPointer(v1Act.BalanceId) + } + if v1Act.BalanceType != "" { + bf.Type = utils.StringPointer(v1Act.BalanceType) + } + if v1Act.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(v1Act.BalanceRatingSubject) + } + if v1Act.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(v1Act.BalanceDirection)) + } + if v1Act.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(v1Act.BalanceDestinationIds)) + } + if v1Act.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(v1Act.BalanceTimingTags)) + } + if v1Act.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(v1Act.BalanceCategory)) + } + if v1Act.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(v1Act.BalanceSharedGroup)) + } + if v1Act.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(v1Act.BalanceWeight) + } + if v1Act.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(v1Act.BalanceDisabled) + } + if !v1Act.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(v1Act.BalanceExpirationDate) + } + at.Balance = bf + if at.ThresholdType == "*min_counter" || + at.ThresholdType == "*max_counter" { + at.ThresholdType = strings.Replace(at.ThresholdType, "_", "_event_", 1) + } + return +} diff --git a/migrator/action_trigger_test.go b/migrator/action_trigger_test.go new file mode 100644 index 000000000..e9cc646e8 --- /dev/null +++ b/migrator/action_trigger_test.go @@ -0,0 +1,50 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var v1ActionTriggers1 = `{"BalanceType": "*monetary","BalanceDirection": "*out","ThresholdType":"*max_balance", "ThresholdValue" :2, "ActionsId": "TEST_ACTIONS", "Executed": true}` + +func TestV1ActionTriggersAsActionTriggers(t *testing.T) { + atrs := &engine.ActionTrigger{ + Balance: &engine.BalanceFilter{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, + ActionsID: "TEST_ACTIONS", + Executed: true, + } + var v1actstrgrs v1ActionTrigger + if err := json.Unmarshal([]byte(v1ActionTriggers1), &v1actstrgrs); err != nil { + t.Error(err) + } + newatrs := v1actstrgrs.AsActionTrigger() + if !reflect.DeepEqual(*atrs, newatrs) { + t.Errorf("Expecting: %+v, received: %+v", *atrs, newatrs) + } +} diff --git a/migrator/costdetails.go b/migrator/costdetails.go index 7d2755b61..67095994d 100644 --- a/migrator/costdetails.go +++ b/migrator/costdetails.go @@ -89,8 +89,8 @@ func (m *Migrator) migrateCostDetails() (err error) { v1CC := &v1CallCost{Direction: ccDirection.String, Category: ccCategory.String, Tenant: ccTenant.String, Subject: ccSubject.String, Account: ccAccount.String, Destination: ccDestination.String, TOR: ccTor.String, Cost: ccCost.Float64, Timespans: v1tmsps} - cc, err := v1CC.AsCallCost() - if err != nil { + cc := v1CC.AsCallCost() + if cc == nil { utils.Logger.Warning( fmt.Sprintf(" Error: <%s> when converting into CallCost CDR with id: <%d>", err.Error(), id)) continue @@ -152,7 +152,7 @@ type v1UnitInfo struct { TOR string } -func (v1cc *v1CallCost) AsCallCost() (cc *engine.CallCost, err error) { +func (v1cc *v1CallCost) AsCallCost() (cc *engine.CallCost) { cc = new(engine.CallCost) cc.Direction = v1cc.Direction cc.Category = v1cc.Category diff --git a/migrator/costdetails_test.go b/migrator/costdetails_test.go index 84d8297de..9b101bbdf 100644 --- a/migrator/costdetails_test.go +++ b/migrator/costdetails_test.go @@ -31,9 +31,7 @@ func TestV1CostDetailsAsCostDetails1(t *testing.T) { t.Error(err) } v1CC := &v1CallCost{Timespans: v1tmsps} - if _, err := v1CC.AsCallCost(); err != nil { - t.Error(err) - } + _ = v1CC.AsCallCost() // ToDo: Test here the content } @@ -44,7 +42,6 @@ func TestV1CostDetailsAsCostDetails2(t *testing.T) { t.Error(err) } v1CC := &v1CallCost{Timespans: v1tmsps} - if _, err := v1CC.AsCallCost(); err != nil { - t.Error(err) - } + _ = v1CC.AsCallCost() + } diff --git a/migrator/sharedgroup.go b/migrator/sharedgroup.go new file mode 100644 index 000000000..e728e46db --- /dev/null +++ b/migrator/sharedgroup.go @@ -0,0 +1,41 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type v1SharedGroup struct { + Id string + AccountParameters map[string]*engine.SharingParameters + MemberIds []string +} + +func (v1SG v1SharedGroup) AsSharedGroup() (sg engine.SharedGroup) { + sg = engine.SharedGroup{ + Id: v1SG.Id, + AccountParameters: v1SG.AccountParameters, + MemberIds: make(utils.StringMap), + } + for _, accID := range v1SG.MemberIds { + sg.MemberIds[accID] = true + } + return +} diff --git a/migrator/sharedgroup_test.go b/migrator/sharedgroup_test.go new file mode 100644 index 000000000..bc7085b16 --- /dev/null +++ b/migrator/sharedgroup_test.go @@ -0,0 +1,48 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package migrator + +import ( + "reflect" + "testing" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func TestV1SharedGroupAsSharedGroup(t *testing.T) { + v1sg := &v1SharedGroup{ + Id: "Test", + AccountParameters: map[string]*engine.SharingParameters{ + "test": &engine.SharingParameters{Strategy: "*highest"}, + }, + MemberIds: []string{"1", "2", "3"}, + } + sg := &engine.SharedGroup{ + Id: "Test", + AccountParameters: map[string]*engine.SharingParameters{ + "test": &engine.SharingParameters{Strategy: "*highest"}, + }, + MemberIds: utils.NewStringMap("1", "2", "3"), + } + newsg := v1sg.AsSharedGroup() + if !reflect.DeepEqual(*sg, newsg) { + t.Errorf("Expecting: %+v, received: %+v", *sg, newsg) + } + +}