/* /* 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 Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package engine import ( "fmt" "reflect" "regexp" "strconv" "strings" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) func csvLoad(s any, values []string) (any, error) { fieldValueMap := make(map[string]string) st := reflect.TypeOf(s) numFields := st.NumField() for i := 0; i < numFields; i++ { field := st.Field(i) re := field.Tag.Get("re") index := field.Tag.Get("index") if index != utils.EmptyString { idx, err := strconv.Atoi(index) if err != nil || len(values) <= idx { return nil, fmt.Errorf("invalid %v.%v index %v", st.Name(), field.Name, index) } if re != utils.EmptyString { if matched, err := regexp.MatchString(re, values[idx]); !matched || err != nil { return nil, fmt.Errorf("invalid %v.%v value %v", st.Name(), field.Name, values[idx]) } } fieldValueMap[field.Name] = values[idx] } } elem := reflect.New(st).Elem() for fieldName, fieldValue := range fieldValueMap { field := elem.FieldByName(fieldName) if field.IsValid() { switch field.Kind() { case reflect.Float64: if fieldValue == utils.EmptyString { fieldValue = "0" } value, err := strconv.ParseFloat(fieldValue, 64) if err != nil { return nil, fmt.Errorf(`invalid value "%s" for field %s.%s`, fieldValue, st.Name(), fieldName) } field.SetFloat(value) case reflect.Int: if fieldValue == utils.EmptyString { fieldValue = "0" } value, err := strconv.Atoi(fieldValue) if err != nil { return nil, fmt.Errorf(`invalid value "%s" for field %s.%s`, fieldValue, st.Name(), fieldName) } field.SetInt(int64(value)) case reflect.Bool: if fieldValue == utils.EmptyString { fieldValue = "false" } value, err := strconv.ParseBool(fieldValue) if err != nil { return nil, fmt.Errorf(`invalid value "%s" for field %s.%s`, fieldValue, st.Name(), fieldName) } field.SetBool(value) case reflect.String: field.SetString(fieldValue) } } } return elem.Interface(), nil } // CsvDump receive and interface and convert it to a slice of string func CsvDump(s any) ([]string, error) { fieldIndexMap := make(map[string]int) st := reflect.ValueOf(s) if st.Kind() == reflect.Ptr { st = st.Elem() s = st.Interface() } numFields := st.NumField() stCopy := reflect.TypeOf(s) for i := 0; i < numFields; i++ { field := stCopy.Field(i) index := field.Tag.Get("index") if index != utils.EmptyString { if idx, err := strconv.Atoi(index); err != nil { return nil, fmt.Errorf("invalid %v.%v index %v", stCopy.Name(), field.Name, index) } else { fieldIndexMap[field.Name] = idx } } } result := make([]string, len(fieldIndexMap)) for fieldName, fieldIndex := range fieldIndexMap { field := st.FieldByName(fieldName) if field.IsValid() && fieldIndex < len(result) { switch field.Kind() { case reflect.Float64: result[fieldIndex] = strconv.FormatFloat(field.Float(), 'f', -1, 64) case reflect.Int: result[fieldIndex] = strconv.FormatInt(field.Int(), 10) case reflect.Bool: result[fieldIndex] = strconv.FormatBool(field.Bool()) case reflect.String: result[fieldIndex] = field.String() } } } return result, nil } func getColumnCount(s any) int { st := reflect.TypeOf(s) numFields := st.NumField() count := 0 for i := 0; i < numFields; i++ { field := st.Field(i) index := field.Tag.Get("index") if index != utils.EmptyString { count++ } } return count } type DestinationMdls []DestinationMdl func (tps DestinationMdls) AsMapDestinations() (map[string]*Destination, error) { result := make(map[string]*Destination) for _, tp := range tps { var d *Destination var found bool if d, found = result[tp.Tag]; !found { d = &Destination{Id: tp.Tag} result[tp.Tag] = d } d.AddPrefix(tp.Prefix) } return result, nil } // AsTPDestination converts DestinationMdls into *utils.TPDestination func (tps DestinationMdls) AsTPDestinations() (result []*utils.TPDestination) { md := make(map[string]*utils.TPDestination) // Should save us some CPU if we index here for big number of destinations to search for _, tp := range tps { if d, hasIt := md[tp.Tag]; !hasIt { md[tp.Tag] = &utils.TPDestination{TPid: tp.Tpid, ID: tp.Tag, Prefixes: []string{tp.Prefix}} } else { d.Prefixes = append(d.Prefixes, tp.Prefix) } } result = make([]*utils.TPDestination, len(md)) i := 0 for _, d := range md { result[i] = d i++ } return } func APItoModelDestination(d *utils.TPDestination) (result DestinationMdls) { if d != nil { for _, p := range d.Prefixes { result = append(result, DestinationMdl{ Tpid: d.TPid, Tag: d.ID, Prefix: p, }) } if len(d.Prefixes) == 0 { result = append(result, DestinationMdl{ Tpid: d.TPid, Tag: d.ID, }) } } return } type TimingMdls []TimingMdl func (tps TimingMdls) AsMapTPTimings() (map[string]*utils.ApierTPTiming, error) { result := make(map[string]*utils.ApierTPTiming) for _, tp := range tps { t := &utils.ApierTPTiming{ TPid: tp.Tpid, ID: tp.Tag, Years: tp.Years, Months: tp.Months, MonthDays: tp.MonthDays, WeekDays: tp.WeekDays, Time: tp.Time, } result[tp.Tag] = t } return result, nil } func MapTPTimings(tps []*utils.ApierTPTiming) (map[string]*utils.TPTiming, error) { result := make(map[string]*utils.TPTiming) for _, tp := range tps { t := utils.NewTiming(tp.ID, tp.Years, tp.Months, tp.MonthDays, tp.WeekDays, tp.Time) if _, found := result[tp.ID]; found { return nil, fmt.Errorf("duplicate timing tag: %s", tp.ID) } result[tp.ID] = t } return result, nil } func (tps TimingMdls) AsTPTimings() (result []*utils.ApierTPTiming) { ats, _ := tps.AsMapTPTimings() for _, tp := range ats { result = append(result, tp) } return result } func APItoModelTiming(t *utils.ApierTPTiming) (result TimingMdl) { return TimingMdl{ Tpid: t.TPid, Tag: t.ID, Years: t.Years, Months: t.Months, MonthDays: t.MonthDays, WeekDays: t.WeekDays, Time: t.Time, } } func APItoModelTimings(ts []*utils.ApierTPTiming) (result TimingMdls) { for _, t := range ts { if t != nil { at := APItoModelTiming(t) result = append(result, at) } } return result } type RateMdls []RateMdl func (tps RateMdls) AsMapRates() (map[string]*utils.TPRateRALs, error) { result := make(map[string]*utils.TPRateRALs) for _, tp := range tps { r := &utils.TPRateRALs{ TPid: tp.Tpid, ID: tp.Tag, } rs := &utils.RateSlot{ ConnectFee: tp.ConnectFee, Rate: tp.Rate, RateUnit: tp.RateUnit, RateIncrement: tp.RateIncrement, GroupIntervalStart: tp.GroupIntervalStart, } if err := rs.SetDurations(); err != nil { return nil, err } if existing, exists := result[r.ID]; !exists { r.RateSlots = []*utils.RateSlot{rs} result[r.ID] = r } else { existing.RateSlots = append(existing.RateSlots, rs) } } return result, nil } func (tps RateMdls) AsTPRates() (result []*utils.TPRateRALs, err error) { if atps, err := tps.AsMapRates(); err != nil { return nil, err } else { for _, tp := range atps { result = append(result, tp) } return result, nil } } func MapTPRates(s []*utils.TPRateRALs) (map[string]*utils.TPRateRALs, error) { result := make(map[string]*utils.TPRateRALs) for _, e := range s { if _, found := result[e.ID]; !found { result[e.ID] = e } else { return nil, fmt.Errorf("Non unique ID %+v", e.ID) } } return result, nil } func APItoModelRate(r *utils.TPRateRALs) (result RateMdls) { if r != nil { for _, rs := range r.RateSlots { result = append(result, RateMdl{ Tpid: r.TPid, Tag: r.ID, ConnectFee: rs.ConnectFee, Rate: rs.Rate, RateUnit: rs.RateUnit, RateIncrement: rs.RateIncrement, GroupIntervalStart: rs.GroupIntervalStart, }) } if len(r.RateSlots) == 0 { result = append(result, RateMdl{ Tpid: r.TPid, Tag: r.ID, }) } } return } func APItoModelRates(rs []*utils.TPRateRALs) (result RateMdls) { for _, r := range rs { result = append(result, APItoModelRate(r)...) } return result } type DestinationRateMdls []DestinationRateMdl func (tps DestinationRateMdls) AsMapDestinationRates() (result map[string]*utils.TPDestinationRate) { result = make(map[string]*utils.TPDestinationRate) for _, tp := range tps { dr := &utils.TPDestinationRate{ TPid: tp.Tpid, ID: tp.Tag, DestinationRates: []*utils.DestinationRate{ { DestinationId: tp.DestinationsTag, RateId: tp.RatesTag, RoundingMethod: tp.RoundingMethod, RoundingDecimals: tp.RoundingDecimals, MaxCost: tp.MaxCost, MaxCostStrategy: tp.MaxCostStrategy, }, }, } existing, exists := result[tp.Tag] if exists { existing.DestinationRates = append(existing.DestinationRates, dr.DestinationRates[0]) } else { existing = dr } result[tp.Tag] = existing } return } func (tps DestinationRateMdls) AsTPDestinationRates() (result []*utils.TPDestinationRate) { for _, tp := range tps.AsMapDestinationRates() { result = append(result, tp) } return } func MapTPDestinationRates(s []*utils.TPDestinationRate) (map[string]*utils.TPDestinationRate, error) { result := make(map[string]*utils.TPDestinationRate) for _, e := range s { if _, found := result[e.ID]; !found { result[e.ID] = e } else { return nil, fmt.Errorf("Non unique ID %+v", e.ID) } } return result, nil } func APItoModelDestinationRate(d *utils.TPDestinationRate) (result DestinationRateMdls) { if d != nil { for _, dr := range d.DestinationRates { result = append(result, DestinationRateMdl{ Tpid: d.TPid, Tag: d.ID, DestinationsTag: dr.DestinationId, RatesTag: dr.RateId, RoundingMethod: dr.RoundingMethod, RoundingDecimals: dr.RoundingDecimals, MaxCost: dr.MaxCost, MaxCostStrategy: dr.MaxCostStrategy, }) } if len(d.DestinationRates) == 0 { result = append(result, DestinationRateMdl{ Tpid: d.TPid, Tag: d.ID, }) } } return } func APItoModelDestinationRates(drs []*utils.TPDestinationRate) (result DestinationRateMdls) { for _, dr := range drs { result = append(result, APItoModelDestinationRate(dr)...) } return result } type RatingPlanMdls []RatingPlanMdl func (tps RatingPlanMdls) AsMapTPRatingPlans() (result map[string]*utils.TPRatingPlan) { result = make(map[string]*utils.TPRatingPlan) for _, tp := range tps { rp := &utils.TPRatingPlan{ TPid: tp.Tpid, ID: tp.Tag, } rpb := &utils.TPRatingPlanBinding{ DestinationRatesId: tp.DestratesTag, TimingId: tp.TimingTag, Weight: tp.Weight, } if existing, exists := result[rp.ID]; !exists { rp.RatingPlanBindings = []*utils.TPRatingPlanBinding{rpb} result[rp.ID] = rp } else { existing.RatingPlanBindings = append(existing.RatingPlanBindings, rpb) } } return } func (tps RatingPlanMdls) AsTPRatingPlans() (result []*utils.TPRatingPlan) { for _, tp := range tps.AsMapTPRatingPlans() { result = append(result, tp) } return } func GetRateInterval(rpl *utils.TPRatingPlanBinding, dr *utils.DestinationRate) (i *RateInterval) { i = &RateInterval{ Timing: &RITiming{ ID: rpl.Timing().ID, Years: rpl.Timing().Years, Months: rpl.Timing().Months, MonthDays: rpl.Timing().MonthDays, WeekDays: rpl.Timing().WeekDays, StartTime: rpl.Timing().StartTime, tag: rpl.Timing().ID, }, Weight: rpl.Weight, Rating: &RIRate{ ConnectFee: dr.Rate.RateSlots[0].ConnectFee, RoundingMethod: dr.RoundingMethod, RoundingDecimals: dr.RoundingDecimals, MaxCost: dr.MaxCost, MaxCostStrategy: dr.MaxCostStrategy, tag: dr.Rate.ID, }, } for _, rl := range dr.Rate.RateSlots { i.Rating.Rates = append(i.Rating.Rates, &RGRate{ GroupIntervalStart: rl.GroupIntervalStartDuration(), Value: rl.Rate, RateIncrement: rl.RateIncrementDuration(), RateUnit: rl.RateUnitDuration(), }) } return } func MapTPRatingPlanBindings(s []*utils.TPRatingPlan) map[string][]*utils.TPRatingPlanBinding { result := make(map[string][]*utils.TPRatingPlanBinding) for _, e := range s { for _, rpb := range e.RatingPlanBindings { if _, found := result[e.ID]; !found { result[e.ID] = []*utils.TPRatingPlanBinding{rpb} } else { result[e.ID] = append(result[e.ID], rpb) } } } return result } func APItoModelRatingPlan(rp *utils.TPRatingPlan) (result RatingPlanMdls) { if rp != nil { for _, rpb := range rp.RatingPlanBindings { result = append(result, RatingPlanMdl{ Tpid: rp.TPid, Tag: rp.ID, DestratesTag: rpb.DestinationRatesId, TimingTag: rpb.TimingId, Weight: rpb.Weight, }) } if len(rp.RatingPlanBindings) == 0 { result = append(result, RatingPlanMdl{ Tpid: rp.TPid, Tag: rp.ID, }) } } return } func APItoModelRatingPlans(rps []*utils.TPRatingPlan) (result RatingPlanMdls) { for _, rp := range rps { result = append(result, APItoModelRatingPlan(rp)...) } return result } type RatingProfileMdls []RatingProfileMdl func (tps RatingProfileMdls) AsMapTPRatingProfiles() (result map[string]*utils.TPRatingProfile) { result = make(map[string]*utils.TPRatingProfile) for _, tp := range tps { rp := &utils.TPRatingProfile{ TPid: tp.Tpid, LoadId: tp.Loadid, Tenant: tp.Tenant, Category: tp.Category, Subject: tp.Subject, } ra := &utils.TPRatingActivation{ ActivationTime: tp.ActivationTime, RatingPlanId: tp.RatingPlanTag, FallbackSubjects: tp.FallbackSubjects, } if existing, exists := result[rp.GetId()]; !exists { rp.RatingPlanActivations = []*utils.TPRatingActivation{ra} result[rp.GetId()] = rp } else { existing.RatingPlanActivations = append(existing.RatingPlanActivations, ra) } } return } func (tps RatingProfileMdls) AsTPRatingProfiles() (result []*utils.TPRatingProfile) { for _, tp := range tps.AsMapTPRatingProfiles() { result = append(result, tp) } return } func MapTPRatingProfiles(s []*utils.TPRatingProfile) (map[string]*utils.TPRatingProfile, error) { result := make(map[string]*utils.TPRatingProfile) for _, e := range s { if _, found := result[e.GetId()]; !found { result[e.GetId()] = e } else { return nil, fmt.Errorf("Non unique id %+v", e.GetId()) } } return result, nil } func APItoModelRatingProfile(rp *utils.TPRatingProfile) (result RatingProfileMdls) { if rp != nil { for _, rpa := range rp.RatingPlanActivations { result = append(result, RatingProfileMdl{ Tpid: rp.TPid, Loadid: rp.LoadId, Tenant: rp.Tenant, Category: rp.Category, Subject: rp.Subject, ActivationTime: rpa.ActivationTime, RatingPlanTag: rpa.RatingPlanId, FallbackSubjects: rpa.FallbackSubjects, }) } if len(rp.RatingPlanActivations) == 0 { result = append(result, RatingProfileMdl{ Tpid: rp.TPid, Loadid: rp.LoadId, Tenant: rp.Tenant, Category: rp.Category, Subject: rp.Subject, }) } } return } func APItoModelRatingProfiles(rps []*utils.TPRatingProfile) (result RatingProfileMdls) { for _, rp := range rps { result = append(result, APItoModelRatingProfile(rp)...) } return result } type SharedGroupMdls []SharedGroupMdl func (tps SharedGroupMdls) AsMapTPSharedGroups() (result map[string]*utils.TPSharedGroups) { result = make(map[string]*utils.TPSharedGroups) for _, tp := range tps { sgs := &utils.TPSharedGroups{ TPid: tp.Tpid, ID: tp.Tag, } sg := &utils.TPSharedGroup{ Account: tp.Account, Strategy: tp.Strategy, RatingSubject: tp.RatingSubject, } if existing, exists := result[sgs.ID]; !exists { sgs.SharedGroups = []*utils.TPSharedGroup{sg} result[sgs.ID] = sgs } else { existing.SharedGroups = append(existing.SharedGroups, sg) } } return } func (tps SharedGroupMdls) AsTPSharedGroups() (result []*utils.TPSharedGroups) { for _, tp := range tps.AsMapTPSharedGroups() { result = append(result, tp) } return } func MapTPSharedGroup(s []*utils.TPSharedGroups) map[string][]*utils.TPSharedGroup { result := make(map[string][]*utils.TPSharedGroup) for _, e := range s { for _, sg := range e.SharedGroups { if _, found := result[e.ID]; !found { result[e.ID] = []*utils.TPSharedGroup{sg} } else { result[e.ID] = append(result[e.ID], sg) } } } return result } func APItoModelSharedGroup(sgs *utils.TPSharedGroups) (result SharedGroupMdls) { if sgs != nil { for _, sg := range sgs.SharedGroups { result = append(result, SharedGroupMdl{ Tpid: sgs.TPid, Tag: sgs.ID, Account: sg.Account, Strategy: sg.Strategy, RatingSubject: sg.RatingSubject, }) } if len(sgs.SharedGroups) == 0 { result = append(result, SharedGroupMdl{ Tpid: sgs.TPid, Tag: sgs.ID, }) } } return } func APItoModelSharedGroups(sgs []*utils.TPSharedGroups) (result SharedGroupMdls) { for _, sg := range sgs { result = append(result, APItoModelSharedGroup(sg)...) } return result } type ActionMdls []ActionMdl func (tps ActionMdls) AsMapTPActions() (result map[string]*utils.TPActions) { result = make(map[string]*utils.TPActions) for _, tp := range tps { as := &utils.TPActions{ TPid: tp.Tpid, ID: tp.Tag, } a := &utils.TPAction{ Identifier: tp.Action, BalanceId: tp.BalanceTag, BalanceType: tp.BalanceType, Units: tp.Units, ExpiryTime: tp.ExpiryTime, Filters: tp.Filters, TimingTags: tp.TimingTags, DestinationIds: tp.DestinationTags, RatingSubject: tp.RatingSubject, Categories: tp.Categories, SharedGroups: tp.SharedGroups, BalanceWeight: tp.BalanceWeight, BalanceBlocker: tp.BalanceBlocker, BalanceDisabled: tp.BalanceDisabled, ExtraParameters: tp.ExtraParameters, Weight: tp.Weight, } if existing, exists := result[as.ID]; !exists { as.Actions = []*utils.TPAction{a} result[as.ID] = as } else { existing.Actions = append(existing.Actions, a) } } return } func (tps ActionMdls) AsTPActions() (result []*utils.TPActions) { for _, tp := range tps.AsMapTPActions() { result = append(result, tp) } return result } func MapTPActions(s []*utils.TPActions) map[string][]*utils.TPAction { result := make(map[string][]*utils.TPAction) for _, e := range s { for _, a := range e.Actions { if _, found := result[e.ID]; !found { result[e.ID] = []*utils.TPAction{a} } else { result[e.ID] = append(result[e.ID], a) } } } return result } func APItoModelAction(as *utils.TPActions) (result ActionMdls) { if as != nil { for _, a := range as.Actions { result = append(result, ActionMdl{ Tpid: as.TPid, Tag: as.ID, Action: a.Identifier, BalanceTag: a.BalanceId, BalanceType: a.BalanceType, Units: a.Units, ExpiryTime: a.ExpiryTime, Filters: a.Filters, TimingTags: a.TimingTags, DestinationTags: a.DestinationIds, RatingSubject: a.RatingSubject, Categories: a.Categories, SharedGroups: a.SharedGroups, BalanceWeight: a.BalanceWeight, BalanceBlocker: a.BalanceBlocker, BalanceDisabled: a.BalanceDisabled, ExtraParameters: a.ExtraParameters, Weight: a.Weight, }) } if len(as.Actions) == 0 { result = append(result, ActionMdl{ Tpid: as.TPid, Tag: as.ID, }) } } return } func APItoModelActions(as []*utils.TPActions) (result ActionMdls) { for _, a := range as { result = append(result, APItoModelAction(a)...) } return result } type ActionPlanMdls []ActionPlanMdl func (tps ActionPlanMdls) AsMapTPActionPlans() (result map[string]*utils.TPActionPlan) { result = make(map[string]*utils.TPActionPlan) for _, tp := range tps { as := &utils.TPActionPlan{ TPid: tp.Tpid, ID: tp.Tag, } a := &utils.TPActionTiming{ ActionsId: tp.ActionsTag, TimingId: tp.TimingTag, Weight: tp.Weight, } if existing, exists := result[as.ID]; !exists { as.ActionPlan = []*utils.TPActionTiming{a} result[as.ID] = as } else { existing.ActionPlan = append(existing.ActionPlan, a) } } return } func (tps ActionPlanMdls) AsTPActionPlans() (result []*utils.TPActionPlan) { for _, tp := range tps.AsMapTPActionPlans() { result = append(result, tp) } return } func MapTPActionTimings(s []*utils.TPActionPlan) map[string][]*utils.TPActionTiming { result := make(map[string][]*utils.TPActionTiming) for _, e := range s { for _, at := range e.ActionPlan { if _, found := result[e.ID]; !found { result[e.ID] = []*utils.TPActionTiming{at} } else { result[e.ID] = append(result[e.ID], at) } } } return result } func APItoModelActionPlan(a *utils.TPActionPlan) (result ActionPlanMdls) { if a != nil { for _, ap := range a.ActionPlan { result = append(result, ActionPlanMdl{ Tpid: a.TPid, Tag: a.ID, ActionsTag: ap.ActionsId, TimingTag: ap.TimingId, Weight: ap.Weight, }) } if len(a.ActionPlan) == 0 { result = append(result, ActionPlanMdl{ Tpid: a.TPid, Tag: a.ID, }) } } return } func APItoModelActionPlans(aps []*utils.TPActionPlan) (result ActionPlanMdls) { for _, ap := range aps { result = append(result, APItoModelActionPlan(ap)...) } return result } type ActionTriggerMdls []ActionTriggerMdl func (tps ActionTriggerMdls) AsMapTPActionTriggers() (result map[string]*utils.TPActionTriggers) { result = make(map[string]*utils.TPActionTriggers) for _, tp := range tps { ats := &utils.TPActionTriggers{ TPid: tp.Tpid, ID: tp.Tag, } at := &utils.TPActionTrigger{ Id: tp.Tag, UniqueID: tp.UniqueId, ThresholdType: tp.ThresholdType, ThresholdValue: tp.ThresholdValue, Recurrent: tp.Recurrent, MinSleep: tp.MinSleep, ExpirationDate: tp.ExpiryTime, ActivationDate: tp.ActivationTime, BalanceId: tp.BalanceTag, BalanceType: tp.BalanceType, BalanceDestinationIds: tp.BalanceDestinationTags, BalanceWeight: tp.BalanceWeight, BalanceExpirationDate: tp.BalanceExpiryTime, BalanceTimingTags: tp.BalanceTimingTags, BalanceRatingSubject: tp.BalanceRatingSubject, BalanceCategories: tp.BalanceCategories, BalanceSharedGroups: tp.BalanceSharedGroups, BalanceBlocker: tp.BalanceBlocker, BalanceDisabled: tp.BalanceDisabled, Weight: tp.Weight, ActionsId: tp.ActionsTag, } if existing, exists := result[ats.ID]; !exists { ats.ActionTriggers = []*utils.TPActionTrigger{at} result[ats.ID] = ats } else { existing.ActionTriggers = append(existing.ActionTriggers, at) } } return } func (tps ActionTriggerMdls) AsTPActionTriggers() (result []*utils.TPActionTriggers) { for _, tp := range tps.AsMapTPActionTriggers() { result = append(result, tp) } return } func MapTPActionTriggers(s []*utils.TPActionTriggers) map[string][]*utils.TPActionTrigger { result := make(map[string][]*utils.TPActionTrigger) for _, e := range s { for _, at := range e.ActionTriggers { if _, found := result[e.ID]; !found { result[e.ID] = []*utils.TPActionTrigger{at} } else { result[e.ID] = append(result[e.ID], at) } } } return result } func APItoModelActionTrigger(ats *utils.TPActionTriggers) (result ActionTriggerMdls) { if ats != nil { for _, at := range ats.ActionTriggers { result = append(result, ActionTriggerMdl{ Tpid: ats.TPid, Tag: ats.ID, UniqueId: at.UniqueID, ThresholdType: at.ThresholdType, ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, ExpiryTime: at.ExpirationDate, ActivationTime: at.ActivationDate, BalanceTag: at.BalanceId, BalanceType: at.BalanceType, BalanceDestinationTags: at.BalanceDestinationIds, BalanceWeight: at.BalanceWeight, BalanceExpiryTime: at.BalanceExpirationDate, BalanceTimingTags: at.BalanceTimingTags, BalanceRatingSubject: at.BalanceRatingSubject, BalanceCategories: at.BalanceCategories, BalanceSharedGroups: at.BalanceSharedGroups, BalanceBlocker: at.BalanceBlocker, BalanceDisabled: at.BalanceDisabled, ActionsTag: at.ActionsId, Weight: at.Weight, }) } if len(ats.ActionTriggers) == 0 { result = append(result, ActionTriggerMdl{ Tpid: ats.TPid, Tag: ats.ID, }) } } return } func APItoModelActionTriggers(ts []*utils.TPActionTriggers) (result ActionTriggerMdls) { for _, t := range ts { result = append(result, APItoModelActionTrigger(t)...) } return result } type AccountActionMdls []AccountActionMdl func (tps AccountActionMdls) AsMapTPAccountActions() (map[string]*utils.TPAccountActions, error) { result := make(map[string]*utils.TPAccountActions) for _, tp := range tps { aas := &utils.TPAccountActions{ TPid: tp.Tpid, LoadId: tp.Loadid, Tenant: tp.Tenant, Account: tp.Account, ActionPlanId: tp.ActionPlanTag, ActionTriggersId: tp.ActionTriggersTag, AllowNegative: tp.AllowNegative, Disabled: tp.Disabled, } result[aas.KeyId()] = aas } return result, nil } func (tps AccountActionMdls) AsTPAccountActions() (result []*utils.TPAccountActions) { atps, _ := tps.AsMapTPAccountActions() for _, tp := range atps { result = append(result, tp) } return result } func MapTPAccountActions(s []*utils.TPAccountActions) (map[string]*utils.TPAccountActions, error) { result := make(map[string]*utils.TPAccountActions) for _, e := range s { if _, found := result[e.KeyId()]; !found { result[e.KeyId()] = e } else { return nil, fmt.Errorf("Non unique ID %+v", e.KeyId()) } } return result, nil } func APItoModelAccountAction(aa *utils.TPAccountActions) *AccountActionMdl { return &AccountActionMdl{ Tpid: aa.TPid, Loadid: aa.LoadId, Tenant: aa.Tenant, Account: aa.Account, ActionPlanTag: aa.ActionPlanId, ActionTriggersTag: aa.ActionTriggersId, AllowNegative: aa.AllowNegative, Disabled: aa.Disabled, } } func APItoModelAccountActions(aas []*utils.TPAccountActions) (result AccountActionMdls) { for _, aa := range aas { if aa != nil { result = append(result, *APItoModelAccountAction(aa)) } } return result } type ResourceMdls []*ResourceMdl // CSVHeader return the header for csv fields as a slice of string func (tps ResourceMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.UsageTTL, utils.Limit, utils.AllocationMessage, utils.Blocker, utils.Stored, utils.Weight, utils.ThresholdIDs} } func (tps ResourceMdls) AsTPResources() (result []*utils.TPResourceProfile) { mrl := make(map[string]*utils.TPResourceProfile) filterMap := make(map[string]utils.StringSet) thresholdMap := make(map[string]utils.StringSet) for _, tp := range tps { tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() rl, found := mrl[tenID] if !found { rl = &utils.TPResourceProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, Blocker: tp.Blocker, Stored: tp.Stored, } } if tp.UsageTTL != utils.EmptyString { rl.UsageTTL = tp.UsageTTL } if tp.Weight != 0 { rl.Weight = tp.Weight } if tp.Limit != utils.EmptyString { rl.Limit = tp.Limit } if tp.AllocationMessage != utils.EmptyString { rl.AllocationMessage = tp.AllocationMessage } rl.Blocker = tp.Blocker rl.Stored = tp.Stored if len(tp.ActivationInterval) != 0 { rl.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { rl.ActivationInterval.ActivationTime = aiSplt[0] rl.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { rl.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.ThresholdIDs != utils.EmptyString { if _, has := thresholdMap[tenID]; !has { thresholdMap[tenID] = make(utils.StringSet) } thresholdMap[tenID].AddSlice(strings.Split(tp.ThresholdIDs, utils.InfieldSep)) } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[tenID]; !has { filterMap[tenID] = make(utils.StringSet) } filterMap[tenID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } mrl[tenID] = rl } result = make([]*utils.TPResourceProfile, len(mrl)) i := 0 for tntID, rl := range mrl { result[i] = rl result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].ThresholdIDs = thresholdMap[tntID].AsSlice() i++ } return } func APItoModelResource(rl *utils.TPResourceProfile) (mdls ResourceMdls) { if rl == nil { return } // In case that TPResourceProfile don't have filter if len(rl.FilterIDs) == 0 { mdl := &ResourceMdl{ Tpid: rl.TPid, Tenant: rl.Tenant, ID: rl.ID, Blocker: rl.Blocker, Stored: rl.Stored, UsageTTL: rl.UsageTTL, Weight: rl.Weight, Limit: rl.Limit, AllocationMessage: rl.AllocationMessage, } if rl.ActivationInterval != nil { if rl.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = rl.ActivationInterval.ActivationTime } if rl.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + rl.ActivationInterval.ExpiryTime } } for i, val := range rl.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } mdls = append(mdls, mdl) } for i, fltr := range rl.FilterIDs { mdl := &ResourceMdl{ Tpid: rl.TPid, Tenant: rl.Tenant, ID: rl.ID, Blocker: rl.Blocker, Stored: rl.Stored, } if i == 0 { mdl.UsageTTL = rl.UsageTTL mdl.Weight = rl.Weight mdl.Limit = rl.Limit mdl.AllocationMessage = rl.AllocationMessage if rl.ActivationInterval != nil { if rl.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = rl.ActivationInterval.ActivationTime } if rl.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + rl.ActivationInterval.ExpiryTime } } for i, val := range rl.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } } mdl.FilterIDs = fltr mdls = append(mdls, mdl) } return } func APItoResource(tpRL *utils.TPResourceProfile, timezone string) (rp *ResourceProfile, err error) { rp = &ResourceProfile{ Tenant: tpRL.Tenant, ID: tpRL.ID, Weight: tpRL.Weight, Blocker: tpRL.Blocker, Stored: tpRL.Stored, AllocationMessage: tpRL.AllocationMessage, ThresholdIDs: make([]string, len(tpRL.ThresholdIDs)), FilterIDs: make([]string, len(tpRL.FilterIDs)), } if tpRL.UsageTTL != utils.EmptyString { if rp.UsageTTL, err = utils.ParseDurationWithNanosecs(tpRL.UsageTTL); err != nil { return nil, err } } copy(rp.FilterIDs, tpRL.FilterIDs) copy(rp.ThresholdIDs, tpRL.ThresholdIDs) if tpRL.ActivationInterval != nil { if rp.ActivationInterval, err = tpRL.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } if tpRL.Limit != utils.EmptyString { if rp.Limit, err = strconv.ParseFloat(tpRL.Limit, 64); err != nil { return nil, err } } return rp, nil } func ResourceProfileToAPI(rp *ResourceProfile) (tpRL *utils.TPResourceProfile) { tpRL = &utils.TPResourceProfile{ Tenant: rp.Tenant, ID: rp.ID, FilterIDs: make([]string, len(rp.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), Limit: strconv.FormatFloat(rp.Limit, 'f', -1, 64), AllocationMessage: rp.AllocationMessage, Blocker: rp.Blocker, Stored: rp.Stored, Weight: rp.Weight, ThresholdIDs: make([]string, len(rp.ThresholdIDs)), } if rp.UsageTTL != time.Duration(0) { tpRL.UsageTTL = rp.UsageTTL.String() } copy(tpRL.FilterIDs, rp.FilterIDs) copy(tpRL.ThresholdIDs, rp.ThresholdIDs) if rp.ActivationInterval != nil { if !rp.ActivationInterval.ActivationTime.IsZero() { tpRL.ActivationInterval.ActivationTime = rp.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !rp.ActivationInterval.ExpiryTime.IsZero() { tpRL.ActivationInterval.ExpiryTime = rp.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type IPMdls []*IPMdl // CSVHeader return the header for csv fields as a slice of string func (tps IPMdls) CSVHeader() []string { return []string{ "#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.TTL, utils.Stored, utils.Weight, utils.PoolID, utils.PoolFilterIDs, utils.PoolType, utils.PoolRange, utils.PoolStrategy, utils.PoolMessage, utils.PoolWeight, utils.PoolBlocker, } } func (tps IPMdls) AsTPIPs() []*utils.TPIPProfile { filterMap := make(map[string]utils.StringSet) mst := make(map[string]*utils.TPIPProfile) poolMap := make(map[string]map[string]*utils.TPIPPool) for _, mdl := range tps { tenID := (&utils.TenantID{Tenant: mdl.Tenant, ID: mdl.ID}).TenantID() tpip, found := mst[tenID] if !found { tpip = &utils.TPIPProfile{ TPid: mdl.Tpid, Tenant: mdl.Tenant, ID: mdl.ID, Stored: mdl.Stored, } } // Handle Pool if mdl.PoolID != utils.EmptyString { if _, has := poolMap[tenID]; !has { poolMap[tenID] = make(map[string]*utils.TPIPPool) } poolID := mdl.PoolID if mdl.PoolFilterIDs != utils.EmptyString { poolID = utils.ConcatenatedKey(poolID, utils.NewStringSet(strings.Split(mdl.PoolFilterIDs, utils.InfieldSep)).Sha1()) } pool, found := poolMap[tenID][poolID] if !found { pool = &utils.TPIPPool{ ID: mdl.PoolID, Type: mdl.PoolType, Range: mdl.PoolRange, Strategy: mdl.PoolStrategy, Message: mdl.PoolMessage, Weight: mdl.PoolWeight, Blocker: mdl.PoolBlocker, } } if mdl.PoolFilterIDs != utils.EmptyString { poolFilterSplit := strings.Split(mdl.PoolFilterIDs, utils.InfieldSep) pool.FilterIDs = append(pool.FilterIDs, poolFilterSplit...) } poolMap[tenID][poolID] = pool } // Profile-level fields if mdl.TTL != utils.EmptyString { tpip.TTL = mdl.TTL } if mdl.Weight != 0 { tpip.Weight = mdl.Weight } if mdl.Stored { tpip.Stored = mdl.Stored } if len(mdl.ActivationInterval) != 0 { tpip.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(mdl.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { tpip.ActivationInterval.ActivationTime = aiSplt[0] tpip.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { tpip.ActivationInterval.ActivationTime = aiSplt[0] } } if mdl.FilterIDs != utils.EmptyString { if _, has := filterMap[tenID]; !has { filterMap[tenID] = make(utils.StringSet) } filterMap[tenID].AddSlice(strings.Split(mdl.FilterIDs, utils.InfieldSep)) } mst[tenID] = tpip } // Build result with Pools result := make([]*utils.TPIPProfile, len(mst)) i := 0 for tntID, tpip := range mst { result[i] = tpip for _, poolData := range poolMap[tntID] { result[i].Pools = append(result[i].Pools, poolData) } result[i].FilterIDs = filterMap[tntID].AsSlice() i++ } return result } func APItoModelIP(tp *utils.TPIPProfile) IPMdls { if tp == nil { return nil } var mdls IPMdls // Handle case with no pools if len(tp.Pools) == 0 { mdl := &IPMdl{ Tpid: tp.TPid, Tenant: tp.Tenant, ID: tp.ID, TTL: tp.TTL, Stored: tp.Stored, Weight: tp.Weight, } if tp.ActivationInterval != nil { if tp.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = tp.ActivationInterval.ActivationTime } if tp.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + tp.ActivationInterval.ExpiryTime } } for i, val := range tp.FilterIDs { if i != 0 { mdl.FilterIDs += utils.InfieldSep } mdl.FilterIDs += val } mdls = append(mdls, mdl) return mdls } for i, pool := range tp.Pools { mdl := &IPMdl{ Tpid: tp.TPid, Tenant: tp.Tenant, ID: tp.ID, Stored: tp.Stored, } if i == 0 { // Profile-level fields only on first row mdl.TTL = tp.TTL mdl.Weight = tp.Weight for j, val := range tp.FilterIDs { if j != 0 { mdl.FilterIDs += utils.InfieldSep } mdl.FilterIDs += val } if tp.ActivationInterval != nil { if tp.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = tp.ActivationInterval.ActivationTime } if tp.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + tp.ActivationInterval.ExpiryTime } } } // Pool fields on every row mdl.PoolID = pool.ID mdl.PoolType = pool.Type mdl.PoolRange = pool.Range mdl.PoolStrategy = pool.Strategy mdl.PoolMessage = pool.Message mdl.PoolWeight = pool.Weight mdl.PoolBlocker = pool.Blocker for j, val := range pool.FilterIDs { if j != 0 { mdl.PoolFilterIDs += utils.InfieldSep } mdl.PoolFilterIDs += val } mdls = append(mdls, mdl) } return mdls } func APItoIP(tp *utils.TPIPProfile, timezone string) (*IPProfile, error) { ipp := &IPProfile{ Tenant: tp.Tenant, ID: tp.ID, Weight: tp.Weight, Stored: tp.Stored, FilterIDs: make([]string, len(tp.FilterIDs)), Pools: make([]*IPPool, len(tp.Pools)), } if tp.TTL != utils.EmptyString { var err error if ipp.TTL, err = utils.ParseDurationWithNanosecs(tp.TTL); err != nil { return nil, err } } copy(ipp.FilterIDs, tp.FilterIDs) if tp.ActivationInterval != nil { var err error if ipp.ActivationInterval, err = tp.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } for i, pool := range tp.Pools { ipp.Pools[i] = &IPPool{ ID: pool.ID, FilterIDs: pool.FilterIDs, Type: pool.Type, Range: pool.Range, Strategy: pool.Strategy, Message: pool.Message, Weight: pool.Weight, Blocker: pool.Blocker, } } return ipp, nil } func IPProfileToAPI(ipp *IPProfile) *utils.TPIPProfile { tp := &utils.TPIPProfile{ Tenant: ipp.Tenant, ID: ipp.ID, FilterIDs: make([]string, len(ipp.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), Stored: ipp.Stored, Weight: ipp.Weight, Pools: make([]*utils.TPIPPool, len(ipp.Pools)), } if ipp.TTL != time.Duration(0) { tp.TTL = ipp.TTL.String() } copy(tp.FilterIDs, ipp.FilterIDs) for i, pool := range ipp.Pools { tp.Pools[i] = &utils.TPIPPool{ ID: pool.ID, FilterIDs: pool.FilterIDs, Type: pool.Type, Range: pool.Range, Strategy: pool.Strategy, Message: pool.Message, Weight: pool.Weight, Blocker: pool.Blocker, } } if ipp.ActivationInterval != nil { if !ipp.ActivationInterval.ActivationTime.IsZero() { tp.ActivationInterval.ActivationTime = ipp.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !ipp.ActivationInterval.ExpiryTime.IsZero() { tp.ActivationInterval.ExpiryTime = ipp.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return tp } type StatMdls []*StatMdl // CSVHeader return the header for csv fields as a slice of string func (tps StatMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.QueueLength, utils.TTL, utils.MinItems, utils.MetricIDs, utils.MetricFilterIDs, utils.Stored, utils.Blocker, utils.Weight, utils.ThresholdIDs} } func (models StatMdls) AsTPStats() (result []*utils.TPStatProfile) { filterMap := make(map[string]utils.StringSet) thresholdMap := make(map[string]utils.StringSet) statMetricsMap := make(map[string]map[string]*utils.MetricWithFilters) mst := make(map[string]*utils.TPStatProfile) for _, model := range models { key := &utils.TenantID{Tenant: model.Tenant, ID: model.ID} st, found := mst[key.TenantID()] if !found { st = &utils.TPStatProfile{ Tenant: model.Tenant, TPid: model.Tpid, ID: model.ID, Blocker: model.Blocker, Stored: model.Stored, Weight: model.Weight, MinItems: model.MinItems, TTL: model.TTL, QueueLength: model.QueueLength, } } if model.Blocker { st.Blocker = model.Blocker } if model.Stored { st.Stored = model.Stored } if model.Weight != 0 { st.Weight = model.Weight } if model.MinItems != 0 { st.MinItems = model.MinItems } if model.TTL != utils.EmptyString { st.TTL = model.TTL } if model.QueueLength != 0 { st.QueueLength = model.QueueLength } if model.ThresholdIDs != utils.EmptyString { if _, has := thresholdMap[key.TenantID()]; !has { thresholdMap[key.TenantID()] = make(utils.StringSet) } thresholdMap[key.TenantID()].AddSlice(strings.Split(model.ThresholdIDs, utils.InfieldSep)) } if len(model.ActivationInterval) != 0 { st.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(model.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { st.ActivationInterval.ActivationTime = aiSplt[0] st.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { st.ActivationInterval.ActivationTime = aiSplt[0] } } if model.FilterIDs != utils.EmptyString { if _, has := filterMap[key.TenantID()]; !has { filterMap[key.TenantID()] = make(utils.StringSet) } filterMap[key.TenantID()].AddSlice(strings.Split(model.FilterIDs, utils.InfieldSep)) } if model.MetricIDs != utils.EmptyString { if _, has := statMetricsMap[key.TenantID()]; !has { statMetricsMap[key.TenantID()] = make(map[string]*utils.MetricWithFilters) } metricIDsSplit := strings.Split(model.MetricIDs, utils.InfieldSep) for _, metricID := range metricIDsSplit { stsMetric, found := statMetricsMap[key.TenantID()][metricID] if !found { stsMetric = &utils.MetricWithFilters{ MetricID: metricID, } } if model.MetricFilterIDs != utils.EmptyString { filterIDs := strings.Split(model.MetricFilterIDs, utils.InfieldSep) stsMetric.FilterIDs = append(stsMetric.FilterIDs, filterIDs...) } statMetricsMap[key.TenantID()][metricID] = stsMetric } } mst[key.TenantID()] = st } result = make([]*utils.TPStatProfile, len(mst)) i := 0 for tntID, st := range mst { result[i] = st result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].ThresholdIDs = thresholdMap[tntID].AsSlice() for _, metric := range statMetricsMap[tntID] { result[i].Metrics = append(result[i].Metrics, metric) } i++ } return } func APItoModelStats(st *utils.TPStatProfile) (mdls StatMdls) { if st != nil && len(st.Metrics) != 0 { for i, metric := range st.Metrics { mdl := &StatMdl{ Tpid: st.TPid, Tenant: st.Tenant, ID: st.ID, } if i == 0 { for i, val := range st.FilterIDs { if i != 0 { mdl.FilterIDs += utils.InfieldSep } mdl.FilterIDs += val } if st.ActivationInterval != nil { if st.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = st.ActivationInterval.ActivationTime } if st.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + st.ActivationInterval.ExpiryTime } } mdl.QueueLength = st.QueueLength mdl.TTL = st.TTL mdl.MinItems = st.MinItems mdl.Stored = st.Stored mdl.Blocker = st.Blocker mdl.Weight = st.Weight for i, val := range st.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } } for i, val := range metric.FilterIDs { if i != 0 { mdl.MetricFilterIDs += utils.InfieldSep } mdl.MetricFilterIDs += val } mdl.MetricIDs = metric.MetricID mdls = append(mdls, mdl) } } return } func APItoStats(tpST *utils.TPStatProfile, timezone string) (st *StatQueueProfile, err error) { st = &StatQueueProfile{ Tenant: tpST.Tenant, ID: tpST.ID, FilterIDs: make([]string, len(tpST.FilterIDs)), QueueLength: tpST.QueueLength, MinItems: tpST.MinItems, Metrics: make([]*MetricWithFilters, len(tpST.Metrics)), Stored: tpST.Stored, Blocker: tpST.Blocker, Weight: tpST.Weight, ThresholdIDs: make([]string, len(tpST.ThresholdIDs)), } if tpST.TTL != utils.EmptyString { if st.TTL, err = utils.ParseDurationWithNanosecs(tpST.TTL); err != nil { return nil, err } } for i, metric := range tpST.Metrics { st.Metrics[i] = &MetricWithFilters{ MetricID: metric.MetricID, FilterIDs: metric.FilterIDs, } } copy(st.ThresholdIDs, tpST.ThresholdIDs) copy(st.FilterIDs, tpST.FilterIDs) if tpST.ActivationInterval != nil { if st.ActivationInterval, err = tpST.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return st, nil } func StatQueueProfileToAPI(st *StatQueueProfile) (tpST *utils.TPStatProfile) { tpST = &utils.TPStatProfile{ Tenant: st.Tenant, ID: st.ID, FilterIDs: make([]string, len(st.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), QueueLength: st.QueueLength, Metrics: make([]*utils.MetricWithFilters, len(st.Metrics)), Blocker: st.Blocker, Stored: st.Stored, Weight: st.Weight, MinItems: st.MinItems, ThresholdIDs: make([]string, len(st.ThresholdIDs)), } for i, metric := range st.Metrics { tpST.Metrics[i] = &utils.MetricWithFilters{ MetricID: metric.MetricID, } if len(metric.FilterIDs) != 0 { tpST.Metrics[i].FilterIDs = make([]string, len(metric.FilterIDs)) copy(tpST.Metrics[i].FilterIDs, metric.FilterIDs) } } if st.TTL != time.Duration(0) { tpST.TTL = st.TTL.String() } copy(tpST.FilterIDs, st.FilterIDs) copy(tpST.ThresholdIDs, st.ThresholdIDs) if st.ActivationInterval != nil { if !st.ActivationInterval.ActivationTime.IsZero() { tpST.ActivationInterval.ActivationTime = st.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !st.ActivationInterval.ExpiryTime.IsZero() { tpST.ActivationInterval.ExpiryTime = st.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type RankingsMdls []*RankingsMdl func (tps RankingsMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.Schedule, utils.StatIDs, utils.MetricIDs, utils.Sorting, utils.SortingParameters, utils.Stored, utils.ThresholdIDs} } func (models RankingsMdls) AsTPRanking() (result []*utils.TPRankingProfile) { thresholdMap := make(map[string]utils.StringSet) metricsMap := make(map[string]utils.StringSet) sortingParameterMap := make(map[string]utils.StringSet) sortingParameterSlice := make(map[string][]string) statsMap := make(map[string]utils.StringSet) mrg := make(map[string]*utils.TPRankingProfile) for _, model := range models { key := &utils.TenantID{Tenant: model.Tenant, ID: model.ID} rg, found := mrg[key.TenantID()] if !found { rg = &utils.TPRankingProfile{ Tenant: model.Tenant, TPid: model.Tpid, ID: model.ID, Schedule: model.Schedule, Sorting: model.Sorting, Stored: model.Stored, } } if model.Schedule != utils.EmptyString { rg.Schedule = model.Schedule } if model.Sorting != utils.EmptyString { rg.Sorting = model.Sorting } if model.Stored { rg.Stored = model.Stored } if model.StatIDs != utils.EmptyString { if _, has := statsMap[key.TenantID()]; !has { statsMap[key.TenantID()] = make(utils.StringSet) } statsMap[key.TenantID()].AddSlice(strings.Split(model.StatIDs, utils.InfieldSep)) } if model.ThresholdIDs != utils.EmptyString { if _, has := thresholdMap[key.TenantID()]; !has { thresholdMap[key.TenantID()] = make(utils.StringSet) } thresholdMap[key.TenantID()].AddSlice(strings.Split(model.ThresholdIDs, utils.InfieldSep)) } if model.SortingParameters != utils.EmptyString { if _, has := sortingParameterMap[key.TenantID()]; !has { sortingParameterMap[key.TenantID()] = make(utils.StringSet) sortingParameterSlice[key.TenantID()] = make([]string, 0) } spltSl := strings.Split(model.SortingParameters, utils.InfieldSep) for _, splt := range spltSl { if _, has := sortingParameterMap[key.TenantID()][splt]; !has { sortingParameterMap[key.TenantID()].Add(splt) sortingParameterSlice[key.TenantID()] = append(sortingParameterSlice[key.TenantID()], splt) } } } if model.MetricIDs != utils.EmptyString { if _, has := metricsMap[key.TenantID()]; !has { metricsMap[key.TenantID()] = make(utils.StringSet) } metricsMap[key.TenantID()].AddSlice(strings.Split(model.MetricIDs, utils.InfieldSep)) } mrg[key.TenantID()] = rg } result = make([]*utils.TPRankingProfile, len(mrg)) i := 0 for tntID, rg := range mrg { result[i] = rg result[i].StatIDs = statsMap[tntID].AsSlice() result[i].MetricIDs = metricsMap[tntID].AsSlice() result[i].SortingParameters = sortingParameterSlice[tntID] result[i].ThresholdIDs = thresholdMap[tntID].AsOrderedSlice() i++ } return } func APItoModelTPRanking(tpRG *utils.TPRankingProfile) (mdls RankingsMdls) { if tpRG == nil { return } if len(tpRG.StatIDs) == 0 { mdl := &RankingsMdl{ Tpid: tpRG.TPid, Tenant: tpRG.Tenant, ID: tpRG.ID, Schedule: tpRG.Schedule, Sorting: tpRG.Sorting, Stored: tpRG.Stored, } for i, val := range tpRG.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } for i, metric := range tpRG.MetricIDs { if i != 0 { mdl.MetricIDs += utils.InfieldSep } mdl.MetricIDs += metric } for i, sorting := range tpRG.SortingParameters { if i != 0 { mdl.SortingParameters += utils.InfieldSep } mdl.SortingParameters += sorting } mdls = append(mdls, mdl) } for i, stat := range tpRG.StatIDs { mdl := &RankingsMdl{ Tpid: tpRG.TPid, Tenant: tpRG.Tenant, ID: tpRG.ID, } if i == 0 { mdl.Schedule = tpRG.Schedule mdl.Sorting = tpRG.Sorting for i, val := range tpRG.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } for i, metric := range tpRG.MetricIDs { if i != 0 { mdl.MetricIDs += utils.InfieldSep } mdl.MetricIDs += metric } for i, sorting := range tpRG.SortingParameters { if i != 0 { mdl.SortingParameters += utils.InfieldSep } mdl.SortingParameters += sorting } } mdl.StatIDs = stat mdls = append(mdls, mdl) } return } func APItoRanking(tpRG *utils.TPRankingProfile) (rg *RankingProfile, err error) { rg = &RankingProfile{ Tenant: tpRG.Tenant, ID: tpRG.ID, Schedule: tpRG.Schedule, Sorting: tpRG.Sorting, Stored: tpRG.Stored, StatIDs: make([]string, len(tpRG.StatIDs)), MetricIDs: make([]string, len(tpRG.MetricIDs)), SortingParameters: make([]string, len(tpRG.SortingParameters)), ThresholdIDs: make([]string, len(tpRG.ThresholdIDs)), } copy(rg.StatIDs, tpRG.StatIDs) copy(rg.ThresholdIDs, tpRG.ThresholdIDs) copy(rg.SortingParameters, tpRG.SortingParameters) copy(rg.MetricIDs, tpRG.MetricIDs) return rg, nil } func RankingProfileToAPI(rg *RankingProfile) (tpRG *utils.TPRankingProfile) { tpRG = &utils.TPRankingProfile{ Tenant: rg.Tenant, ID: rg.ID, Schedule: rg.Schedule, Sorting: rg.Sorting, Stored: rg.Stored, StatIDs: make([]string, len(rg.StatIDs)), MetricIDs: make([]string, len(rg.MetricIDs)), SortingParameters: make([]string, len(rg.SortingParameters)), ThresholdIDs: make([]string, len(rg.ThresholdIDs)), } copy(tpRG.StatIDs, rg.StatIDs) copy(tpRG.ThresholdIDs, rg.ThresholdIDs) copy(tpRG.MetricIDs, rg.MetricIDs) copy(tpRG.SortingParameters, rg.SortingParameters) return } type TrendsMdls []*TrendsMdl func (tps TrendsMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.Schedule, utils.StatID, utils.Metrics, utils.TTL, utils.QueueLength, utils.MinItems, utils.CorrelationType, utils.Tolerance, utils.Stored, utils.ThresholdIDs} } func (models TrendsMdls) AsTPTrends() (result []*utils.TPTrendsProfile) { thresholdsMap := make(map[string]utils.StringSet) trendMetricsMap := make(map[string]utils.StringSet) mtr := make(map[string]*utils.TPTrendsProfile) for _, model := range models { key := &utils.TenantID{Tenant: model.Tenant, ID: model.ID} tr, found := mtr[key.TenantID()] if !found { tr = &utils.TPTrendsProfile{ Tenant: model.Tenant, TPid: model.Tpid, ID: model.ID, Schedule: model.Schedule, StatID: model.StatID, TTL: model.TTL, QueueLength: model.QueueLength, MinItems: model.MinItems, Tolerance: model.Tolerance, Stored: model.Stored, CorrelationType: model.CorrelationType, } } if model.Schedule != utils.EmptyString { tr.Schedule = model.Schedule } if model.StatID != utils.EmptyString { tr.StatID = model.StatID } if model.TTL != utils.EmptyString { tr.TTL = model.TTL } if model.QueueLength != 0 { tr.QueueLength = model.QueueLength } if model.MinItems != 0 { tr.MinItems = model.MinItems } if model.CorrelationType != utils.EmptyString { tr.CorrelationType = model.CorrelationType } if model.Tolerance != 0 { tr.Tolerance = model.Tolerance } if model.Stored { tr.Stored = true } if model.ThresholdIDs != utils.EmptyString { if _, has := thresholdsMap[key.TenantID()]; !has { thresholdsMap[key.TenantID()] = make(utils.StringSet) } thresholdsMap[key.TenantID()].AddSlice(strings.Split(model.ThresholdIDs, utils.InfieldSep)) } if model.Metrics != utils.EmptyString { if _, has := trendMetricsMap[key.TenantID()]; !has { trendMetricsMap[key.TenantID()] = make(utils.StringSet) } trendMetricsMap[key.TenantID()].AddSlice(strings.Split(model.Metrics, utils.InfieldSep)) } mtr[key.TenantID()] = tr } result = make([]*utils.TPTrendsProfile, len(mtr)) i := 0 for tntId, sr := range mtr { result[i] = sr result[i].ThresholdIDs = thresholdsMap[tntId].AsSlice() result[i].Metrics = trendMetricsMap[tntId].AsSlice() i++ } return } func APItoModelTrends(tr *utils.TPTrendsProfile) (mdls TrendsMdls) { if tr != nil { mdl := &TrendsMdl{ Tpid: tr.TPid, Tenant: tr.Tenant, ID: tr.ID, } mdl.Schedule = tr.Schedule mdl.QueueLength = tr.QueueLength mdl.StatID = tr.StatID mdl.TTL = tr.TTL mdl.MinItems = tr.MinItems mdl.CorrelationType = tr.CorrelationType mdl.Tolerance = tr.Tolerance mdl.Stored = tr.Stored for i, val := range tr.ThresholdIDs { if i != 0 { mdl.ThresholdIDs += utils.InfieldSep } mdl.ThresholdIDs += val } for i, val := range tr.Metrics { if i != 0 { mdl.Metrics += utils.InfieldSep } mdl.Metrics += val } mdls = append(mdls, mdl) } return } func APItoTrends(tpTR *utils.TPTrendsProfile) (tr *TrendProfile, err error) { tr = &TrendProfile{ Tenant: tpTR.Tenant, ID: tpTR.ID, StatID: tpTR.StatID, Schedule: tpTR.Schedule, QueueLength: tpTR.QueueLength, ThresholdIDs: make([]string, len(tpTR.ThresholdIDs)), Metrics: make([]string, len(tpTR.Metrics)), MinItems: tpTR.MinItems, CorrelationType: tpTR.CorrelationType, Tolerance: tpTR.Tolerance, } if tpTR.TTL != utils.EmptyString { if tr.TTL, err = utils.ParseDurationWithNanosecs(tpTR.TTL); err != nil { return } } copy(tr.ThresholdIDs, tpTR.ThresholdIDs) copy(tr.Metrics, tpTR.Metrics) return } func TrendProfileToAPI(tr *TrendProfile) (tpTR *utils.TPTrendsProfile) { tpTR = &utils.TPTrendsProfile{ Tenant: tr.Tenant, ID: tr.ID, Schedule: tr.Schedule, StatID: tr.StatID, ThresholdIDs: make([]string, len(tr.ThresholdIDs)), Metrics: make([]string, len(tr.Metrics)), QueueLength: tr.QueueLength, MinItems: tr.MinItems, CorrelationType: tr.CorrelationType, Tolerance: tr.Tolerance, Stored: tr.Stored, } if tr.TTL != time.Duration(0) { tpTR.TTL = tr.TTL.String() } copy(tpTR.ThresholdIDs, tr.ThresholdIDs) copy(tpTR.Metrics, tr.Metrics) return } type ThresholdMdls []*ThresholdMdl // CSVHeader return the header for csv fields as a slice of string func (tps ThresholdMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.MaxHits, utils.MinHits, utils.MinSleep, utils.Blocker, utils.Weight, utils.ActionIDs, utils.Async, utils.EeIDs} } func (tps ThresholdMdls) AsTPThreshold() (result []*utils.TPThresholdProfile) { mst := make(map[string]*utils.TPThresholdProfile) filterMap := make(map[string]utils.StringSet) actionMap := make(map[string]utils.StringSet) eeIDsMap := make(map[string]utils.StringSet) for _, tp := range tps { tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() th, found := mst[tenID] if !found { th = &utils.TPThresholdProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, Blocker: tp.Blocker, MaxHits: tp.MaxHits, MinHits: tp.MinHits, MinSleep: tp.MinSleep, Async: tp.Async, } } if tp.ActionIDs != utils.EmptyString { if _, has := actionMap[tenID]; !has { actionMap[tenID] = make(utils.StringSet) } actionMap[tenID].AddSlice(strings.Split(tp.ActionIDs, utils.InfieldSep)) } if tp.EeIDs != utils.EmptyString { if _, has := eeIDsMap[tenID]; !has { eeIDsMap[tenID] = make(utils.StringSet) } eeIDsMap[tenID].AddSlice(strings.Split(tp.EeIDs, utils.InfieldSep)) } if tp.Weight != 0 { th.Weight = tp.Weight } if len(tp.ActivationInterval) != 0 { th.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { th.ActivationInterval.ActivationTime = aiSplt[0] th.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { th.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[tenID]; !has { filterMap[tenID] = make(utils.StringSet) } filterMap[tenID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } mst[tenID] = th } result = make([]*utils.TPThresholdProfile, len(mst)) i := 0 for tntID, th := range mst { result[i] = th result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].ActionIDs = actionMap[tntID].AsSlice() result[i].EeIDs = eeIDsMap[tntID].AsSlice() i++ } return } func APItoModelTPThreshold(th *utils.TPThresholdProfile) (mdls ThresholdMdls) { if th != nil { if len(th.ActionIDs) == 0 { return } min := len(th.FilterIDs) if min > len(th.ActionIDs) { min = len(th.ActionIDs) } for i := range min { mdl := &ThresholdMdl{ Tpid: th.TPid, Tenant: th.Tenant, ID: th.ID, } if i == 0 { mdl.Blocker = th.Blocker mdl.Weight = th.Weight mdl.MaxHits = th.MaxHits mdl.MinHits = th.MinHits mdl.MinSleep = th.MinSleep mdl.Async = th.Async if th.ActivationInterval != nil { if th.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = th.ActivationInterval.ActivationTime } if th.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + th.ActivationInterval.ExpiryTime } } for i, val := range th.EeIDs { if i != 0 { mdl.EeIDs += utils.InfieldSep } mdl.EeIDs += val } } mdl.FilterIDs = th.FilterIDs[i] mdl.ActionIDs = th.ActionIDs[i] mdls = append(mdls, mdl) } if len(th.FilterIDs)-min > 0 { for i := min; i < len(th.FilterIDs); i++ { mdl := &ThresholdMdl{ Tpid: th.TPid, Tenant: th.Tenant, ID: th.ID, } mdl.FilterIDs = th.FilterIDs[i] mdls = append(mdls, mdl) } } if len(th.ActionIDs)-min > 0 { for i := min; i < len(th.ActionIDs); i++ { mdl := &ThresholdMdl{ Tpid: th.TPid, Tenant: th.Tenant, ID: th.ID, } if min == 0 && i == 0 { mdl.Blocker = th.Blocker mdl.Weight = th.Weight mdl.MaxHits = th.MaxHits mdl.MinHits = th.MinHits mdl.MinSleep = th.MinSleep mdl.Async = th.Async if th.ActivationInterval != nil { if th.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = th.ActivationInterval.ActivationTime } if th.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + th.ActivationInterval.ExpiryTime } } } mdl.ActionIDs = th.ActionIDs[i] mdls = append(mdls, mdl) } } } return } func APItoThresholdProfile(tpTH *utils.TPThresholdProfile, timezone string) (th *ThresholdProfile, err error) { th = &ThresholdProfile{ Tenant: tpTH.Tenant, ID: tpTH.ID, MaxHits: tpTH.MaxHits, MinHits: tpTH.MinHits, Weight: tpTH.Weight, Blocker: tpTH.Blocker, Async: tpTH.Async, ActionIDs: make([]string, len(tpTH.ActionIDs)), FilterIDs: make([]string, len(tpTH.FilterIDs)), } if tpTH.MinSleep != utils.EmptyString { if th.MinSleep, err = utils.ParseDurationWithNanosecs(tpTH.MinSleep); err != nil { return nil, err } } if len(tpTH.EeIDs) > 0 { th.EeIDs = make([]string, len(tpTH.EeIDs)) copy(th.EeIDs, tpTH.EeIDs) } copy(th.ActionIDs, tpTH.ActionIDs) copy(th.FilterIDs, tpTH.FilterIDs) if tpTH.ActivationInterval != nil { if th.ActivationInterval, err = tpTH.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return th, nil } func ThresholdProfileToAPI(th *ThresholdProfile) (tpTH *utils.TPThresholdProfile) { tpTH = &utils.TPThresholdProfile{ Tenant: th.Tenant, ID: th.ID, FilterIDs: make([]string, len(th.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), MaxHits: th.MaxHits, MinHits: th.MinHits, Blocker: th.Blocker, Weight: th.Weight, ActionIDs: make([]string, len(th.ActionIDs)), EeIDs: make([]string, len(th.EeIDs)), Async: th.Async, } if th.MinSleep != time.Duration(0) { tpTH.MinSleep = th.MinSleep.String() } copy(tpTH.FilterIDs, th.FilterIDs) copy(tpTH.ActionIDs, th.ActionIDs) copy(tpTH.EeIDs, th.EeIDs) if th.ActivationInterval != nil { if !th.ActivationInterval.ActivationTime.IsZero() { tpTH.ActivationInterval.ActivationTime = th.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !th.ActivationInterval.ExpiryTime.IsZero() { tpTH.ActivationInterval.ExpiryTime = th.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type FilterMdls []*FilterMdl // CSVHeader return the header for csv fields as a slice of string func (tps FilterMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.Type, utils.Element, utils.Values, utils.ActivationIntervalString} } func (tps FilterMdls) AsTPFilter() (result []*utils.TPFilterProfile) { mst := make(map[string]*utils.TPFilterProfile) filterRules := make(map[string]*utils.TPFilter) for _, tp := range tps { tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() th, found := mst[tenID] if !found { th = &utils.TPFilterProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, } } if len(tp.ActivationInterval) != 0 { th.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { th.ActivationInterval.ActivationTime = aiSplt[0] th.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { th.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.Type != utils.EmptyString { var vals []string if tp.Values != utils.EmptyString { vals = splitDynFltrValues(tp.Values, utils.InfieldSep) } key := utils.ConcatenatedKey(tenID, tp.Type, tp.Element) if f, has := filterRules[key]; has { f.Values = append(f.Values, vals...) } else { f = &utils.TPFilter{ Type: tp.Type, Element: tp.Element, Values: vals, } th.Filters = append(th.Filters, f) filterRules[key] = f } } mst[tenID] = th } result = make([]*utils.TPFilterProfile, len(mst)) i := 0 for _, th := range mst { result[i] = th i++ } return } func APItoModelTPFilter(th *utils.TPFilterProfile) (mdls FilterMdls) { if th == nil || len(th.Filters) == 0 { return } for _, fltr := range th.Filters { mdl := &FilterMdl{ Tpid: th.TPid, Tenant: th.Tenant, ID: th.ID, } mdl.Type = fltr.Type mdl.Element = fltr.Element if th.ActivationInterval != nil { if th.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = th.ActivationInterval.ActivationTime } if th.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + th.ActivationInterval.ExpiryTime } } for i, val := range fltr.Values { if i != 0 { mdl.Values += utils.InfieldSep } mdl.Values += val } mdls = append(mdls, mdl) } return } func APItoFilter(tpTH *utils.TPFilterProfile, timezone string) (th *Filter, err error) { th = &Filter{ Tenant: tpTH.Tenant, ID: tpTH.ID, Rules: make([]*FilterRule, len(tpTH.Filters)), } for i, f := range tpTH.Filters { rf := &FilterRule{Type: f.Type, Element: f.Element, Values: f.Values} if err := rf.CompileValues(); err != nil { return nil, err } th.Rules[i] = rf } if tpTH.ActivationInterval != nil { if th.ActivationInterval, err = tpTH.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return th, nil } func FilterToTPFilter(f *Filter) (tpFltr *utils.TPFilterProfile) { tpFltr = &utils.TPFilterProfile{ Tenant: f.Tenant, ID: f.ID, Filters: make([]*utils.TPFilter, len(f.Rules)), ActivationInterval: new(utils.TPActivationInterval), } for i, reqFltr := range f.Rules { tpFltr.Filters[i] = &utils.TPFilter{ Type: reqFltr.Type, Element: reqFltr.Element, Values: make([]string, len(reqFltr.Values)), } copy(tpFltr.Filters[i].Values, reqFltr.Values) } if f.ActivationInterval != nil { tpFltr.ActivationInterval = &utils.TPActivationInterval{ ActivationTime: f.ActivationInterval.ActivationTime.Format(time.RFC3339), ExpiryTime: f.ActivationInterval.ExpiryTime.Format(time.RFC3339), } } return } type RouteMdls []*RouteMdl // CSVHeader return the header for csv fields as a slice of string func (tps RouteMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.Sorting, utils.SortingParameters, utils.RouteID, utils.RouteFilterIDs, utils.RouteAccountIDs, utils.RouteRatingplanIDs, utils.RouteResourceIDs, utils.RouteStatIDs, utils.RouteWeight, utils.RouteBlocker, utils.RouteParameters, utils.Weight, } } func (tps RouteMdls) AsTPRouteProfile() (result []*utils.TPRouteProfile) { filterMap := make(map[string]utils.StringSet) mst := make(map[string]*utils.TPRouteProfile) routeMap := make(map[string]map[string]*utils.TPRoute) sortingParameterMap := make(map[string]utils.StringSet) for _, tp := range tps { tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() th, found := mst[tenID] if !found { th = &utils.TPRouteProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, SortingParameters: []string{}, } } if tp.RouteID != utils.EmptyString { if _, has := routeMap[tenID]; !has { routeMap[tenID] = make(map[string]*utils.TPRoute) } routeID := tp.RouteID if tp.RouteFilterIDs != utils.EmptyString { routeID = utils.ConcatenatedKey(routeID, utils.NewStringSet(strings.Split(tp.RouteFilterIDs, utils.InfieldSep)).Sha1()) } sup, found := routeMap[tenID][routeID] if !found { sup = &utils.TPRoute{ ID: tp.RouteID, Weight: tp.RouteWeight, Blocker: tp.RouteBlocker, RouteParameters: tp.RouteParameters, } } if tp.RouteFilterIDs != utils.EmptyString { supFilterSplit := strings.Split(tp.RouteFilterIDs, utils.InfieldSep) sup.FilterIDs = append(sup.FilterIDs, supFilterSplit...) } if tp.RouteRatingplanIDs != utils.EmptyString { ratingPlanSplit := strings.Split(tp.RouteRatingplanIDs, utils.InfieldSep) sup.RatingPlanIDs = append(sup.RatingPlanIDs, ratingPlanSplit...) } if tp.RouteResourceIDs != utils.EmptyString { resSplit := strings.Split(tp.RouteResourceIDs, utils.InfieldSep) sup.ResourceIDs = append(sup.ResourceIDs, resSplit...) } if tp.RouteStatIDs != utils.EmptyString { statSplit := strings.Split(tp.RouteStatIDs, utils.InfieldSep) sup.StatIDs = append(sup.StatIDs, statSplit...) } if tp.RouteAccountIDs != utils.EmptyString { accSplit := strings.Split(tp.RouteAccountIDs, utils.InfieldSep) sup.AccountIDs = append(sup.AccountIDs, accSplit...) } routeMap[tenID][routeID] = sup } if tp.Sorting != utils.EmptyString { th.Sorting = tp.Sorting } if tp.SortingParameters != utils.EmptyString { if _, has := sortingParameterMap[tenID]; !has { sortingParameterMap[tenID] = make(utils.StringSet) } sortingParameterMap[tenID].AddSlice(strings.Split(tp.SortingParameters, utils.InfieldSep)) } if tp.Weight != 0 { th.Weight = tp.Weight } if tp.ActivationInterval != utils.EmptyString { th.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { th.ActivationInterval.ActivationTime = aiSplt[0] th.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { th.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[tenID]; !has { filterMap[tenID] = make(utils.StringSet) } filterMap[tenID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } mst[tenID] = th } result = make([]*utils.TPRouteProfile, len(mst)) i := 0 for tntID, th := range mst { result[i] = th for _, supData := range routeMap[tntID] { result[i].Routes = append(result[i].Routes, supData) } result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].SortingParameters = sortingParameterMap[tntID].AsSlice() i++ } return } func APItoModelTPRoutes(st *utils.TPRouteProfile) (mdls RouteMdls) { if len(st.Routes) == 0 { return } for i, supl := range st.Routes { mdl := &RouteMdl{ Tenant: st.Tenant, Tpid: st.TPid, ID: st.ID, } if i == 0 { mdl.Sorting = st.Sorting mdl.Weight = st.Weight for i, val := range st.FilterIDs { if i != 0 { mdl.FilterIDs += utils.InfieldSep } mdl.FilterIDs += val } for i, val := range st.SortingParameters { if i != 0 { mdl.SortingParameters += utils.InfieldSep } mdl.SortingParameters += val } if st.ActivationInterval != nil { if st.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = st.ActivationInterval.ActivationTime } if st.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + st.ActivationInterval.ExpiryTime } } } mdl.RouteID = supl.ID for i, val := range supl.AccountIDs { if i != 0 { mdl.RouteAccountIDs += utils.InfieldSep } mdl.RouteAccountIDs += val } for i, val := range supl.RatingPlanIDs { if i != 0 { mdl.RouteRatingplanIDs += utils.InfieldSep } mdl.RouteRatingplanIDs += val } for i, val := range supl.FilterIDs { if i != 0 { mdl.RouteFilterIDs += utils.InfieldSep } mdl.RouteFilterIDs += val } for i, val := range supl.ResourceIDs { if i != 0 { mdl.RouteResourceIDs += utils.InfieldSep } mdl.RouteResourceIDs += val } for i, val := range supl.StatIDs { if i != 0 { mdl.RouteStatIDs += utils.InfieldSep } mdl.RouteStatIDs += val } mdl.RouteWeight = supl.Weight mdl.RouteParameters = supl.RouteParameters mdl.RouteBlocker = supl.Blocker mdls = append(mdls, mdl) } return } func APItoRouteProfile(tpRp *utils.TPRouteProfile, timezone string) (rp *RouteProfile, err error) { rp = &RouteProfile{ Tenant: tpRp.Tenant, ID: tpRp.ID, Sorting: tpRp.Sorting, Weight: tpRp.Weight, Routes: make([]*Route, len(tpRp.Routes)), SortingParameters: make([]string, len(tpRp.SortingParameters)), FilterIDs: make([]string, len(tpRp.FilterIDs)), } copy(rp.SortingParameters, tpRp.SortingParameters) copy(rp.FilterIDs, tpRp.FilterIDs) if tpRp.ActivationInterval != nil { if rp.ActivationInterval, err = tpRp.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } for i, route := range tpRp.Routes { rp.Routes[i] = &Route{ ID: route.ID, Weight: route.Weight, Blocker: route.Blocker, RatingPlanIDs: route.RatingPlanIDs, AccountIDs: route.AccountIDs, FilterIDs: route.FilterIDs, ResourceIDs: route.ResourceIDs, StatIDs: route.StatIDs, RouteParameters: route.RouteParameters, } } return rp, nil } func RouteProfileToAPI(rp *RouteProfile) (tpRp *utils.TPRouteProfile) { tpRp = &utils.TPRouteProfile{ Tenant: rp.Tenant, ID: rp.ID, FilterIDs: make([]string, len(rp.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), Sorting: rp.Sorting, SortingParameters: make([]string, len(rp.SortingParameters)), Routes: make([]*utils.TPRoute, len(rp.Routes)), Weight: rp.Weight, } for i, route := range rp.Routes { tpRp.Routes[i] = &utils.TPRoute{ ID: route.ID, FilterIDs: route.FilterIDs, AccountIDs: route.AccountIDs, RatingPlanIDs: route.RatingPlanIDs, ResourceIDs: route.ResourceIDs, StatIDs: route.StatIDs, Weight: route.Weight, Blocker: route.Blocker, RouteParameters: route.RouteParameters, } } copy(tpRp.FilterIDs, rp.FilterIDs) copy(tpRp.SortingParameters, rp.SortingParameters) if rp.ActivationInterval != nil { if !rp.ActivationInterval.ActivationTime.IsZero() { tpRp.ActivationInterval.ActivationTime = rp.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !rp.ActivationInterval.ExpiryTime.IsZero() { tpRp.ActivationInterval.ExpiryTime = rp.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type AttributeMdls []*AttributeMdl // CSVHeader return the header for csv fields as a slice of string func (tps AttributeMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.AttributeFilterIDs, utils.Path, utils.Type, utils.Value, utils.Blocker, utils.Weight} } func (tps AttributeMdls) AsTPAttributes() (result []*utils.TPAttributeProfile) { mst := make(map[string]*utils.TPAttributeProfile) filterMap := make(map[string]utils.StringSet) contextMap := make(map[string]utils.StringSet) for _, tp := range tps { key := &utils.TenantID{Tenant: tp.Tenant, ID: tp.ID} tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() th, found := mst[tenID] if !found { th = &utils.TPAttributeProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, Blocker: tp.Blocker, } } if tp.Weight != 0 { th.Weight = tp.Weight } if len(tp.ActivationInterval) != 0 { th.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { th.ActivationInterval.ActivationTime = aiSplt[0] th.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { th.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[key.TenantID()]; !has { filterMap[key.TenantID()] = make(utils.StringSet) } filterMap[key.TenantID()].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } if tp.Contexts != utils.EmptyString { if _, has := contextMap[tenID]; !has { contextMap[key.TenantID()] = make(utils.StringSet) } contextMap[key.TenantID()].AddSlice(strings.Split(tp.Contexts, utils.InfieldSep)) } if tp.Path != utils.EmptyString { filterIDs := make([]string, 0) if tp.AttributeFilterIDs != utils.EmptyString { filterIDs = append(filterIDs, strings.Split(tp.AttributeFilterIDs, utils.InfieldSep)...) } th.Attributes = append(th.Attributes, &utils.TPAttribute{ FilterIDs: filterIDs, Type: tp.Type, Path: tp.Path, Value: tp.Value, }) } mst[key.TenantID()] = th } result = make([]*utils.TPAttributeProfile, len(mst)) i := 0 for tntID, th := range mst { result[i] = th result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].Contexts = contextMap[tntID].AsSlice() i++ } return } func APItoModelTPAttribute(th *utils.TPAttributeProfile) (mdls AttributeMdls) { if len(th.Attributes) == 0 { return } for i, reqAttribute := range th.Attributes { mdl := &AttributeMdl{ Tpid: th.TPid, Tenant: th.Tenant, ID: th.ID, } if i == 0 { mdl.Blocker = th.Blocker if th.ActivationInterval != nil { if th.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = th.ActivationInterval.ActivationTime } if th.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + th.ActivationInterval.ExpiryTime } } for i, val := range th.Contexts { if i != 0 { mdl.Contexts += utils.InfieldSep } mdl.Contexts += val } for i, val := range th.FilterIDs { if i != 0 { mdl.FilterIDs += utils.InfieldSep } mdl.FilterIDs += val } if th.Weight != 0 { mdl.Weight = th.Weight } } mdl.Path = reqAttribute.Path mdl.Value = reqAttribute.Value mdl.Type = reqAttribute.Type mdl.AttributeFilterIDs = strings.Join(reqAttribute.FilterIDs, utils.InfieldSep) mdls = append(mdls, mdl) } return } func APItoAttributeProfile(tpAttr *utils.TPAttributeProfile, timezone string) (attrPrf *AttributeProfile, err error) { attrPrf = &AttributeProfile{ Tenant: tpAttr.Tenant, ID: tpAttr.ID, Weight: tpAttr.Weight, Blocker: tpAttr.Blocker, FilterIDs: make([]string, len(tpAttr.FilterIDs)), Contexts: make([]string, len(tpAttr.Contexts)), Attributes: make([]*Attribute, len(tpAttr.Attributes)), } copy(attrPrf.FilterIDs, tpAttr.FilterIDs) copy(attrPrf.Contexts, tpAttr.Contexts) for i, reqAttr := range tpAttr.Attributes { if reqAttr.Path == utils.EmptyString { // we do not suppot empty Path in Attributes err = fmt.Errorf("empty path in AttributeProfile <%s>", attrPrf.TenantID()) return } sbstPrsr, err := config.NewRSRParsers(reqAttr.Value, config.CgrConfig().GeneralCfg().RSRSep) if err != nil { return nil, err } attrPrf.Attributes[i] = &Attribute{ FilterIDs: reqAttr.FilterIDs, Path: reqAttr.Path, Type: reqAttr.Type, Value: sbstPrsr, } } if tpAttr.ActivationInterval != nil { if attrPrf.ActivationInterval, err = tpAttr.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return attrPrf, nil } func AttributeProfileToAPI(attrPrf *AttributeProfile) (tpAttr *utils.TPAttributeProfile) { tpAttr = &utils.TPAttributeProfile{ Tenant: attrPrf.Tenant, ID: attrPrf.ID, FilterIDs: make([]string, len(attrPrf.FilterIDs)), Contexts: make([]string, len(attrPrf.Contexts)), Attributes: make([]*utils.TPAttribute, len(attrPrf.Attributes)), ActivationInterval: new(utils.TPActivationInterval), Blocker: attrPrf.Blocker, Weight: attrPrf.Weight, } copy(tpAttr.FilterIDs, attrPrf.FilterIDs) copy(tpAttr.Contexts, attrPrf.Contexts) for i, attr := range attrPrf.Attributes { tpAttr.Attributes[i] = &utils.TPAttribute{ FilterIDs: attr.FilterIDs, Path: attr.Path, Type: attr.Type, Value: attr.Value.GetRule(utils.InfieldSep), } } if attrPrf.ActivationInterval != nil { if !attrPrf.ActivationInterval.ActivationTime.IsZero() { tpAttr.ActivationInterval.ActivationTime = attrPrf.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !attrPrf.ActivationInterval.ExpiryTime.IsZero() { tpAttr.ActivationInterval.ExpiryTime = attrPrf.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type ChargerMdls []*ChargerMdl // CSVHeader return the header for csv fields as a slice of string func (tps ChargerMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.FilterIDs, utils.ActivationIntervalString, utils.RunID, utils.AttributeIDs, utils.Weight} } func (tps ChargerMdls) AsTPChargers() (result []*utils.TPChargerProfile) { mst := make(map[string]*utils.TPChargerProfile) filterMap := make(map[string]utils.StringSet) attributeMap := make(map[string][]string) for _, tp := range tps { tntID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() tpCPP, found := mst[tntID] if !found { tpCPP = &utils.TPChargerProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, } } if tp.Weight != 0 { tpCPP.Weight = tp.Weight } if len(tp.ActivationInterval) != 0 { tpCPP.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { tpCPP.ActivationInterval.ActivationTime = aiSplt[0] tpCPP.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { tpCPP.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[tntID]; !has { filterMap[tntID] = make(utils.StringSet) } filterMap[tntID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } if tp.RunID != utils.EmptyString { tpCPP.RunID = tp.RunID } if tp.AttributeIDs != utils.EmptyString { attributeSplit := strings.Split(tp.AttributeIDs, utils.InfieldSep) var inlineAttribute string var dynam bool for _, attribute := range attributeSplit { if !dynam && !strings.HasPrefix(attribute, utils.Meta) { if inlineAttribute != utils.EmptyString { attributeMap[tntID] = append(attributeMap[tntID], inlineAttribute[1:]) inlineAttribute = utils.EmptyString } attributeMap[tntID] = append(attributeMap[tntID], attribute) continue } if dynam { dynam = !strings.Contains(attribute, string(utils.RSRDynEndChar)) } else { dynam = strings.Contains(attribute, string(utils.RSRDynStartChar)) } inlineAttribute += utils.InfieldSep + attribute } if inlineAttribute != utils.EmptyString { attributeMap[tntID] = append(attributeMap[tntID], inlineAttribute[1:]) inlineAttribute = utils.EmptyString } } mst[tntID] = tpCPP } result = make([]*utils.TPChargerProfile, len(mst)) i := 0 for tntID, tp := range mst { result[i] = tp result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].AttributeIDs = make([]string, 0, len(attributeMap[tntID])) result[i].AttributeIDs = append(result[i].AttributeIDs, attributeMap[tntID]...) i++ } return } func APItoModelTPCharger(tpCPP *utils.TPChargerProfile) (mdls ChargerMdls) { if tpCPP != nil { min := len(tpCPP.FilterIDs) isFilter := true if min > len(tpCPP.AttributeIDs) { min = len(tpCPP.AttributeIDs) isFilter = false } if min == 0 { mdl := &ChargerMdl{ Tenant: tpCPP.Tenant, Tpid: tpCPP.TPid, ID: tpCPP.ID, Weight: tpCPP.Weight, RunID: tpCPP.RunID, } if tpCPP.ActivationInterval != nil { if tpCPP.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = tpCPP.ActivationInterval.ActivationTime } if tpCPP.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + tpCPP.ActivationInterval.ExpiryTime } } if isFilter && len(tpCPP.AttributeIDs) > 0 { mdl.AttributeIDs = tpCPP.AttributeIDs[0] } else if len(tpCPP.FilterIDs) > 0 { mdl.FilterIDs = tpCPP.FilterIDs[0] } min = 1 mdls = append(mdls, mdl) } else { for i := 0; i < min; i++ { mdl := &ChargerMdl{ Tenant: tpCPP.Tenant, Tpid: tpCPP.TPid, ID: tpCPP.ID, } if i == 0 { mdl.Weight = tpCPP.Weight mdl.RunID = tpCPP.RunID if tpCPP.ActivationInterval != nil { if tpCPP.ActivationInterval.ActivationTime != utils.EmptyString { mdl.ActivationInterval = tpCPP.ActivationInterval.ActivationTime } if tpCPP.ActivationInterval.ExpiryTime != utils.EmptyString { mdl.ActivationInterval += utils.InfieldSep + tpCPP.ActivationInterval.ExpiryTime } } } mdl.AttributeIDs = tpCPP.AttributeIDs[i] mdl.FilterIDs = tpCPP.FilterIDs[i] mdls = append(mdls, mdl) } } if len(tpCPP.FilterIDs)-min > 0 { for i := min; i < len(tpCPP.FilterIDs); i++ { mdl := &ChargerMdl{ Tenant: tpCPP.Tenant, Tpid: tpCPP.TPid, ID: tpCPP.ID, } mdl.FilterIDs = tpCPP.FilterIDs[i] mdls = append(mdls, mdl) } } if len(tpCPP.AttributeIDs)-min > 0 { for i := min; i < len(tpCPP.AttributeIDs); i++ { mdl := &ChargerMdl{ Tenant: tpCPP.Tenant, Tpid: tpCPP.TPid, ID: tpCPP.ID, } mdl.AttributeIDs = tpCPP.AttributeIDs[i] mdls = append(mdls, mdl) } } } return } func APItoChargerProfile(tpCPP *utils.TPChargerProfile, timezone string) (cpp *ChargerProfile, err error) { cpp = &ChargerProfile{ Tenant: tpCPP.Tenant, ID: tpCPP.ID, Weight: tpCPP.Weight, RunID: tpCPP.RunID, FilterIDs: make([]string, len(tpCPP.FilterIDs)), AttributeIDs: make([]string, len(tpCPP.AttributeIDs)), } copy(cpp.FilterIDs, tpCPP.FilterIDs) copy(cpp.AttributeIDs, tpCPP.AttributeIDs) if tpCPP.ActivationInterval != nil { if cpp.ActivationInterval, err = tpCPP.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return cpp, nil } func ChargerProfileToAPI(chargerPrf *ChargerProfile) (tpCharger *utils.TPChargerProfile) { tpCharger = &utils.TPChargerProfile{ Tenant: chargerPrf.Tenant, ID: chargerPrf.ID, FilterIDs: make([]string, len(chargerPrf.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), RunID: chargerPrf.RunID, AttributeIDs: make([]string, len(chargerPrf.AttributeIDs)), Weight: chargerPrf.Weight, } copy(tpCharger.FilterIDs, chargerPrf.FilterIDs) copy(tpCharger.AttributeIDs, chargerPrf.AttributeIDs) if chargerPrf.ActivationInterval != nil { if !chargerPrf.ActivationInterval.ActivationTime.IsZero() { tpCharger.ActivationInterval.ActivationTime = chargerPrf.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !chargerPrf.ActivationInterval.ExpiryTime.IsZero() { tpCharger.ActivationInterval.ExpiryTime = chargerPrf.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } type DispatcherProfileMdls []*DispatcherProfileMdl // CSVHeader return the header for csv fields as a slice of string func (tps DispatcherProfileMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.Subsystems, utils.FilterIDs, utils.ActivationIntervalString, utils.Strategy, utils.StrategyParameters, utils.ConnID, utils.ConnFilterIDs, utils.ConnWeight, utils.ConnBlocker, utils.ConnParameters, utils.Weight} } func (tps DispatcherProfileMdls) AsTPDispatcherProfiles() (result []*utils.TPDispatcherProfile) { mst := make(map[string]*utils.TPDispatcherProfile) filterMap := make(map[string]utils.StringSet) contextMap := make(map[string]utils.StringSet) connsMap := make(map[string]map[string]utils.TPDispatcherHostProfile) connsFilterMap := make(map[string]map[string]utils.StringSet) for _, tp := range tps { tenantID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() tpDPP, found := mst[tenantID] if !found { tpDPP = &utils.TPDispatcherProfile{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, } } if tp.Subsystems != utils.EmptyString { if _, has := contextMap[tenantID]; !has { contextMap[tenantID] = make(utils.StringSet) } contextMap[tenantID].AddSlice(strings.Split(tp.Subsystems, utils.InfieldSep)) } if tp.FilterIDs != utils.EmptyString { if _, has := filterMap[tenantID]; !has { filterMap[tenantID] = make(utils.StringSet) } filterMap[tenantID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) } if len(tp.ActivationInterval) != 0 { tpDPP.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) if len(aiSplt) == 2 { tpDPP.ActivationInterval.ActivationTime = aiSplt[0] tpDPP.ActivationInterval.ExpiryTime = aiSplt[1] } else if len(aiSplt) == 1 { tpDPP.ActivationInterval.ActivationTime = aiSplt[0] } } if tp.Strategy != utils.EmptyString { tpDPP.Strategy = tp.Strategy } if tp.StrategyParameters != utils.EmptyString { for _, param := range strings.Split(tp.StrategyParameters, utils.InfieldSep) { tpDPP.StrategyParams = append(tpDPP.StrategyParams, param) } } if tp.ConnID != utils.EmptyString { if _, has := connsMap[tenantID]; !has { connsMap[tenantID] = make(map[string]utils.TPDispatcherHostProfile) } conn, has := connsMap[tenantID][tp.ConnID] if !has { conn = utils.TPDispatcherHostProfile{ ID: tp.ConnID, Weight: tp.ConnWeight, Blocker: tp.ConnBlocker, } } for _, param := range strings.Split(tp.ConnParameters, utils.InfieldSep) { conn.Params = append(conn.Params, param) } connsMap[tenantID][tp.ConnID] = conn if dFilter, has := connsFilterMap[tenantID]; !has { connsFilterMap[tenantID] = make(map[string]utils.StringSet) connsFilterMap[tenantID][tp.ConnID] = make(utils.StringSet) } else if _, has := dFilter[tp.ConnID]; !has { connsFilterMap[tenantID][tp.ConnID] = make(utils.StringSet) } if tp.ConnFilterIDs != utils.EmptyString { connsFilterMap[tenantID][tp.ConnID].AddSlice(strings.Split(tp.ConnFilterIDs, utils.InfieldSep)) } } if tp.Weight != 0 { tpDPP.Weight = tp.Weight } mst[tenantID] = tpDPP } result = make([]*utils.TPDispatcherProfile, len(mst)) i := 0 for tntID, tp := range mst { result[i] = tp result[i].FilterIDs = filterMap[tntID].AsSlice() result[i].Subsystems = contextMap[tntID].AsSlice() for conID, conn := range connsMap[tntID] { conn.FilterIDs = connsFilterMap[tntID][conID].AsSlice() result[i].Hosts = append(result[i].Hosts, &utils.TPDispatcherHostProfile{ ID: conn.ID, FilterIDs: conn.FilterIDs, Weight: conn.Weight, Params: conn.Params, Blocker: conn.Blocker, }) } i++ } return } func paramsToString(sp []any) (strategy string) { if len(sp) != 0 { strategy = sp[0].(string) for i := 1; i < len(sp); i++ { strategy += utils.InfieldSep + sp[i].(string) } } return } func APItoModelTPDispatcherProfile(tpDPP *utils.TPDispatcherProfile) (mdls DispatcherProfileMdls) { if tpDPP == nil { return } filters := strings.Join(tpDPP.FilterIDs, utils.InfieldSep) subsystems := strings.Join(tpDPP.Subsystems, utils.InfieldSep) interval := utils.EmptyString if tpDPP.ActivationInterval != nil { if tpDPP.ActivationInterval.ActivationTime != utils.EmptyString { interval = tpDPP.ActivationInterval.ActivationTime } if tpDPP.ActivationInterval.ExpiryTime != utils.EmptyString { interval += utils.InfieldSep + tpDPP.ActivationInterval.ExpiryTime } } strategy := paramsToString(tpDPP.StrategyParams) if len(tpDPP.Hosts) == 0 { return append(mdls, &DispatcherProfileMdl{ Tpid: tpDPP.TPid, Tenant: tpDPP.Tenant, ID: tpDPP.ID, Subsystems: subsystems, FilterIDs: filters, ActivationInterval: interval, Strategy: tpDPP.Strategy, StrategyParameters: strategy, Weight: tpDPP.Weight, }) } conFilter := strings.Join(tpDPP.Hosts[0].FilterIDs, utils.InfieldSep) conParam := paramsToString(tpDPP.Hosts[0].Params) mdls = append(mdls, &DispatcherProfileMdl{ Tpid: tpDPP.TPid, Tenant: tpDPP.Tenant, ID: tpDPP.ID, Subsystems: subsystems, FilterIDs: filters, ActivationInterval: interval, Strategy: tpDPP.Strategy, StrategyParameters: strategy, Weight: tpDPP.Weight, ConnID: tpDPP.Hosts[0].ID, ConnFilterIDs: conFilter, ConnWeight: tpDPP.Hosts[0].Weight, ConnBlocker: tpDPP.Hosts[0].Blocker, ConnParameters: conParam, }) for i := 1; i < len(tpDPP.Hosts); i++ { conFilter = strings.Join(tpDPP.Hosts[i].FilterIDs, utils.InfieldSep) conParam = paramsToString(tpDPP.Hosts[i].Params) mdls = append(mdls, &DispatcherProfileMdl{ Tpid: tpDPP.TPid, Tenant: tpDPP.Tenant, ID: tpDPP.ID, ConnID: tpDPP.Hosts[i].ID, ConnFilterIDs: conFilter, ConnWeight: tpDPP.Hosts[i].Weight, ConnBlocker: tpDPP.Hosts[i].Blocker, ConnParameters: conParam, }) } return } func APItoDispatcherProfile(tpDPP *utils.TPDispatcherProfile, timezone string) (dpp *DispatcherProfile, err error) { dpp = &DispatcherProfile{ Tenant: tpDPP.Tenant, ID: tpDPP.ID, Weight: tpDPP.Weight, Strategy: tpDPP.Strategy, FilterIDs: make([]string, len(tpDPP.FilterIDs)), Subsystems: make([]string, len(tpDPP.Subsystems)), StrategyParams: make(map[string]any), Hosts: make(DispatcherHostProfiles, len(tpDPP.Hosts)), } copy(dpp.FilterIDs, tpDPP.FilterIDs) copy(dpp.Subsystems, tpDPP.Subsystems) for i, param := range tpDPP.StrategyParams { if param != utils.EmptyString { dpp.StrategyParams[strconv.Itoa(i)] = param } } for i, conn := range tpDPP.Hosts { dpp.Hosts[i] = &DispatcherHostProfile{ ID: conn.ID, Weight: conn.Weight, Blocker: conn.Blocker, FilterIDs: make([]string, len(conn.FilterIDs)), Params: make(map[string]any), } copy(dpp.Hosts[i].FilterIDs, conn.FilterIDs) for j, param := range conn.Params { if param == utils.EmptyString { continue } if p := strings.SplitN(utils.IfaceAsString(param), utils.ConcatenatedKeySep, 2); len(p) == 1 { dpp.Hosts[i].Params[strconv.Itoa(j)] = p[0] } else { dpp.Hosts[i].Params[p[0]] = p[1] } } } if tpDPP.ActivationInterval != nil { if dpp.ActivationInterval, err = tpDPP.ActivationInterval.AsActivationInterval(timezone); err != nil { return nil, err } } return dpp, nil } func DispatcherProfileToAPI(dpp *DispatcherProfile) (tpDPP *utils.TPDispatcherProfile) { tpDPP = &utils.TPDispatcherProfile{ Tenant: dpp.Tenant, ID: dpp.ID, Subsystems: make([]string, len(dpp.Subsystems)), FilterIDs: make([]string, len(dpp.FilterIDs)), ActivationInterval: new(utils.TPActivationInterval), Strategy: dpp.Strategy, StrategyParams: make([]any, len(dpp.StrategyParams)), Weight: dpp.Weight, Hosts: make([]*utils.TPDispatcherHostProfile, len(dpp.Hosts)), } copy(tpDPP.FilterIDs, dpp.FilterIDs) copy(tpDPP.Subsystems, dpp.Subsystems) for key, val := range dpp.StrategyParams { // here we expect that the key to be an integer because // according to APItoDispatcherProfile when we convert from TP to obj we use index as key // so we can ignore error idx, _ := strconv.Atoi(key) tpDPP.StrategyParams[idx] = val } for i, host := range dpp.Hosts { tpDPP.Hosts[i] = &utils.TPDispatcherHostProfile{ ID: host.ID, FilterIDs: make([]string, len(host.FilterIDs)), Weight: host.Weight, Params: make([]any, len(host.Params)), Blocker: host.Blocker, } copy(tpDPP.Hosts[i].FilterIDs, host.FilterIDs) idx := 0 for key, val := range host.Params { paramVal := val if _, err := strconv.Atoi(key); err != nil { paramVal = utils.ConcatenatedKey(key, utils.IfaceAsString(val)) } tpDPP.Hosts[i].Params[idx] = paramVal idx++ } } if dpp.ActivationInterval != nil { if !dpp.ActivationInterval.ActivationTime.IsZero() { tpDPP.ActivationInterval.ActivationTime = dpp.ActivationInterval.ActivationTime.Format(time.RFC3339) } if !dpp.ActivationInterval.ExpiryTime.IsZero() { tpDPP.ActivationInterval.ExpiryTime = dpp.ActivationInterval.ExpiryTime.Format(time.RFC3339) } } return } // TPHosts type DispatcherHostMdls []*DispatcherHostMdl // CSVHeader return the header for csv fields as a slice of string func (tps DispatcherHostMdls) CSVHeader() (result []string) { return []string{"#" + utils.Tenant, utils.ID, utils.Address, utils.Transport, utils.SynchronousCfg, utils.ConnectAttemptsCfg, utils.ReconnectsCfg, utils.MaxReconnectIntervalCfg, utils.ConnectTimeoutCfg, utils.ReplyTimeoutCfg, utils.TLS, utils.ClientKeyCfg, utils.ClientCerificateCfg, utils.CaCertificateCfg} } func (tps DispatcherHostMdls) AsTPDispatcherHosts() (result []*utils.TPDispatcherHost, err error) { hostsMap := make(map[string]*utils.TPDispatcherHost) for _, tp := range tps { if len(tp.Address) == 0 { // empty addres do not populate conns continue } if len(tp.Transport) == 0 { tp.Transport = utils.MetaJSON } tntId := utils.ConcatenatedKey(tp.Tenant, tp.ID) hostsMap[tntId] = &utils.TPDispatcherHost{ TPid: tp.Tpid, Tenant: tp.Tenant, ID: tp.ID, Conn: &utils.TPDispatcherHostConn{ Address: tp.Address, Transport: tp.Transport, ConnectAttempts: tp.ConnectAttempts, Reconnects: tp.Reconnects, TLS: tp.TLS, ClientKey: tp.ClientKey, ClientCertificate: tp.ClientCertificate, CaCertificate: tp.CaCertificate, }, } if tp.MaxReconnectInterval != utils.EmptyString { if hostsMap[tntId].Conn.MaxReconnectInterval, err = utils.ParseDurationWithNanosecs(tp.MaxReconnectInterval); err != nil { return nil, err } } if tp.ConnectTimeout != utils.EmptyString { if hostsMap[tntId].Conn.ConnectTimeout, err = utils.ParseDurationWithNanosecs(tp.ConnectTimeout); err != nil { return nil, err } } if tp.ReplyTimeout != utils.EmptyString { if hostsMap[tntId].Conn.ReplyTimeout, err = utils.ParseDurationWithNanosecs(tp.ReplyTimeout); err != nil { return nil, err } } continue } for _, host := range hostsMap { result = append(result, host) } return } func APItoModelTPDispatcherHost(tpDPH *utils.TPDispatcherHost) (mdls *DispatcherHostMdl) { if tpDPH == nil { return } return &DispatcherHostMdl{ Tpid: tpDPH.TPid, Tenant: tpDPH.Tenant, ID: tpDPH.ID, Address: tpDPH.Conn.Address, Transport: tpDPH.Conn.Transport, ConnectAttempts: tpDPH.Conn.ConnectAttempts, Reconnects: tpDPH.Conn.Reconnects, MaxReconnectInterval: tpDPH.Conn.MaxReconnectInterval.String(), ConnectTimeout: tpDPH.Conn.ConnectTimeout.String(), ReplyTimeout: tpDPH.Conn.ReplyTimeout.String(), TLS: tpDPH.Conn.TLS, ClientKey: tpDPH.Conn.ClientKey, ClientCertificate: tpDPH.Conn.ClientCertificate, CaCertificate: tpDPH.Conn.CaCertificate, } } func APItoDispatcherHost(tpDPH *utils.TPDispatcherHost) (dpp *DispatcherHost) { if tpDPH == nil { return } return &DispatcherHost{ Tenant: tpDPH.Tenant, RemoteHost: &config.RemoteHost{ ID: tpDPH.ID, Address: tpDPH.Conn.Address, Transport: tpDPH.Conn.Transport, ConnectAttempts: tpDPH.Conn.ConnectAttempts, Reconnects: tpDPH.Conn.Reconnects, MaxReconnectInterval: tpDPH.Conn.MaxReconnectInterval, ConnectTimeout: tpDPH.Conn.ConnectTimeout, ReplyTimeout: tpDPH.Conn.ReplyTimeout, TLS: tpDPH.Conn.TLS, ClientKey: tpDPH.Conn.ClientKey, ClientCertificate: tpDPH.Conn.ClientCertificate, CaCertificate: tpDPH.Conn.CaCertificate, }, } } func DispatcherHostToAPI(dph *DispatcherHost) (tpDPH *utils.TPDispatcherHost) { return &utils.TPDispatcherHost{ Tenant: dph.Tenant, ID: dph.ID, Conn: &utils.TPDispatcherHostConn{ Address: dph.Address, Transport: dph.Transport, ConnectAttempts: dph.ConnectAttempts, Reconnects: dph.Reconnects, MaxReconnectInterval: dph.MaxReconnectInterval, ConnectTimeout: dph.ConnectTimeout, ReplyTimeout: dph.ReplyTimeout, TLS: dph.TLS, ClientKey: dph.ClientKey, ClientCertificate: dph.ClientCertificate, CaCertificate: dph.CaCertificate, }, } }