diff --git a/apier/v1/apier2_it_test.go b/apier/v1/apier2_it_test.go index 98ea074ec..dac3917e8 100644 --- a/apier/v1/apier2_it_test.go +++ b/apier/v1/apier2_it_test.go @@ -35,9 +35,9 @@ import ( ) var ( - apierCfgPath string - apierCfg *config.CGRConfig - apierRPC *rpc.Client + apierCfgPath string + apierCfg *config.CGRConfig + apierRPC *rpc.Client APIerSv2ConfigDIR string //run tests for specific configuration sTestsAPIer = []func(t *testing.T){ diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go index f5234b907..5735c9e0d 100644 --- a/apier/v1/apier_it_test.go +++ b/apier/v1/apier_it_test.go @@ -59,9 +59,9 @@ README: * Execute remote Apis and test their replies(follow testtp scenario so we can test load in dataDb also). */ var ( - cfgPath string - cfg *config.CGRConfig - rater *rpc.Client + cfgPath string + cfg *config.CGRConfig + rater *rpc.Client APIerSv1ConfigDIR string apierTests = []func(t *testing.T){ diff --git a/apier/v2/apierv2_it_test.go b/apier/v2/apierv2_it_test.go index 3e2218dc1..a15b2c860 100644 --- a/apier/v2/apierv2_it_test.go +++ b/apier/v2/apierv2_it_test.go @@ -34,10 +34,10 @@ import ( ) var ( - apierCfgPath string - apierCfg *config.CGRConfig - apierRPC *rpc.Client - dm *engine.DataManager // share db connection here so we can check data we set through APIs + apierCfgPath string + apierCfg *config.CGRConfig + apierRPC *rpc.Client + dm *engine.DataManager // share db connection here so we can check data we set through APIs APIerSv2ConfDIR string sTestsv2it = []func(t *testing.T){ diff --git a/config/smconfig.go b/config/smconfig.go index f6bb0358b..3c14b5fb0 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -236,6 +236,13 @@ func (scfg *SessionSCfg) loadFromJsonCfg(jsnCfg *SessionSJsonCfg) (err error) { scfg.SessionTTLLastUsed = &sessionTTLLastUsed } } + if jsnCfg.Session_ttl_usage != nil { + if sessionTTLUsage, err := utils.ParseDurationWithNanosecs(*jsnCfg.Session_ttl_usage); err != nil { + return err + } else { + scfg.SessionTTLUsage = &sessionTTLUsage + } + } if jsnCfg.Session_indexes != nil { scfg.SessionIndexes = utils.StringMapFromSlice(*jsnCfg.Session_indexes) } diff --git a/engine/eventcost.go b/engine/eventcost.go index ad56c722c..222249360 100644 --- a/engine/eventcost.go +++ b/engine/eventcost.go @@ -20,11 +20,15 @@ package engine import ( "errors" + "fmt" + "strconv" + "strings" "time" "github.com/cgrates/cgrates/utils" ) +// NewBareEventCost will intialize the EventCost with minimum information func NewBareEventCost() *EventCost { return &EventCost{ Rating: make(Rating), @@ -36,6 +40,7 @@ func NewBareEventCost() *EventCost { } } +// NewEventCostFromCallCost will initilaize the EventCost from a CallCost func NewEventCostFromCallCost(cc *CallCost, cgrID, runID string) (ec *EventCost) { ec = NewBareEventCost() ec.CGRID = cgrID @@ -169,6 +174,7 @@ func (ec *EventCost) rateIntervalForRatingID(ratingID string) (ri *RateInterval) return } +// Clone will create a clone of the object func (ec *EventCost) Clone() (cln *EventCost) { if ec == nil { return @@ -228,7 +234,7 @@ func (ec *EventCost) ResetCounters() { } } -// ComputeCost iterates through Charges, computing EventCost.Cost +// GetCost iterates through Charges, computing EventCost.Cost func (ec *EventCost) GetCost() float64 { if ec.Cost == nil { var cost float64 @@ -241,7 +247,7 @@ func (ec *EventCost) GetCost() float64 { return *ec.Cost } -// ComputeUsage iterates through Charges, computing EventCost.Usage +// GetUsage iterates through Charges, computing EventCost.Usage func (ec *EventCost) GetUsage() time.Duration { if ec.Usage == nil { var usage time.Duration @@ -264,7 +270,7 @@ func (ec *EventCost) ComputeEventCostUsageIndexes() { } } -// AsCallDescriptor converts an EventCost into a CallDescriptor +// AsRefundIncrements converts an EventCost into a CallDescriptor func (ec *EventCost) AsRefundIncrements(tor string) (cd *CallDescriptor) { cd = &CallDescriptor{ CgrID: ec.CGRID, @@ -463,7 +469,7 @@ func (ec *EventCost) appendCIlFromEC(oEC *EventCost, cIlIdx int) { func (ec *EventCost) appendChargingIntervalFromEventCost(oEC *EventCost, cIlIdx int) { lenChargers := len(ec.Charges) if lenChargers != 0 && ec.Charges[lenChargers-1].PartiallyEquals(oEC.Charges[cIlIdx]) { - ec.Charges[lenChargers-1].CompressFactor += 1 + ec.Charges[lenChargers-1].CompressFactor++ } else { ec.appendCIlFromEC(oEC, cIlIdx) } @@ -793,7 +799,7 @@ func (ec *EventCost) Trim(atUsage time.Duration) (srplusEC *EventCost, err error } if len(srplsIncrements) != 0 { // partially covering, need trim if lastActiveCIl.CompressFactor > 1 { // ChargingInterval not covering in full, need to split it - lastActiveCIl.CompressFactor -= 1 + lastActiveCIl.CompressFactor-- ec.Charges = append(ec.Charges, lastActiveCIl.Clone()) lastActiveCIl = ec.Charges[len(ec.Charges)-1] lastActiveCIl.CompressFactor = 1 @@ -833,3 +839,142 @@ func (ec *EventCost) Trim(atUsage time.Duration) (srplusEC *EventCost, err error ec.RemoveStaleReferences() // data should be transferred by now, can clean the old one return } + +// getIndex returns the path and index if index present +// path[index]=>path,index +// path=>path,nil +func getIndex(spath string) (opath string, idx *int) { + idxStart := strings.Index(spath, utils.IdxStart) + if idxStart == -1 || !strings.HasSuffix(spath, utils.IdxEnd) { + return spath, nil + } + slctr := spath[idxStart+1 : len(spath)-1] + opath = spath[:idxStart] + if strings.HasPrefix(slctr, utils.DynamicDataPrefix) { + return + } + idxVal, err := strconv.Atoi(slctr) + if err != nil { + return spath, nil + } + return opath, &idxVal +} + +// FieldAsInterface func to implement DataProvider +func (ec *EventCost) FieldAsInterface(fldPath []string) (val interface{}, err error) { + if len(fldPath) == 0 { + return nil, utils.ErrNotFound + } + switch fldPath[0] { + default: //"Charges [1]" + opath, indx := getIndex(fldPath[0]) + if opath != utils.Charges { + return nil, fmt.Errorf("unsupported field prefix: <%s>", opath) + } + if indx != nil { + chr := ec.Charges[*indx] + if len(fldPath) == 1 { + return chr, nil + } + if fldPath[1] == utils.Rating { + return ec.getRatingForPath(fldPath[2:], ec.Rating[chr.RatingID]) + } + } + case utils.Charges: // not needed? + // return ec.Charges.FieldAsInterface(fldPath[1:]) + case utils.CGRID: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return ec.CGRID, nil + case utils.RunID: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return ec.RunID, nil + case utils.StartTime: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return ec.StartTime, nil + case utils.Usage: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return ec.Usage, nil + case utils.Cost: + if len(fldPath) != 1 { + return nil, utils.ErrNotFound + } + return ec.Cost, nil + case utils.AccountSummary: + // return ec.AccountSummary.FieldAsInterface(fldPath[1:]) + case utils.Timings: // not needed? + // return ec.Timings.FieldAsInterface(fldPath[1:]) + case utils.Rates: // not needed? + // return ec.Rates.FieldAsInterface(fldPath[1:]) + case utils.RatingFilters: // not needed? + // return ec.RatingFilters.FieldAsInterface(fldPath[1:]) + case utils.Accounting: // not needed? + // return ec.Accounting.FieldAsInterface(fldPath[1:]) + case utils.Rating: // not needed? + // return ec.Rating.FieldAsInterface(fldPath[1:]) + } + return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) +} + +func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val interface{}, err error) { + if rating == nil { + return nil, utils.ErrNotFound + } + if len(fldPath) == 0 { + return rating, nil + } + + switch fldPath[0] { + default: + opath, indx := getIndex(fldPath[0]) + if opath != utils.Rates { + return nil, fmt.Errorf("unsupported field prefix: <%s>", opath) + } + rts, has := ec.Rates[rating.RatesID] + if !has || rts == nil { + return nil, utils.ErrNotFound + } + if indx != nil { + rt := rts[*indx] + if len(fldPath) == 1 { + return rt, nil + } + return rt.FieldAsInterface(fldPath[1:]) + } + case utils.Rates: + rts, has := ec.Rates[rating.RatesID] + if !has || rts == nil { + return nil, utils.ErrNotFound + } + if len(fldPath) != 1 { + return nil, utils.ErrNotFound // no field on slice + } + return rts, nil + case utils.Timing: + tmg, has := ec.Timings[rating.TimingID] + if !has || tmg == nil { + return nil, utils.ErrNotFound + } + if len(fldPath) == 1 { + return tmg, nil + } + return tmg.FieldAsInterface(fldPath[1:]) + case utils.RatingFilter: + rtFltr, has := ec.RatingFilters[rating.RatingFiltersID] + if !has || rtFltr == nil { + return nil, utils.ErrNotFound + } + if len(fldPath) == 1 { + return rtFltr, nil + } + return rtFltr.FieldAsInterface(fldPath[1:]) + } + return rating.FieldAsInterface(fldPath) +} diff --git a/engine/suppliers.go b/engine/suppliers.go index 0b186251c..2116a6c2e 100644 --- a/engine/suppliers.go +++ b/engine/suppliers.go @@ -160,8 +160,7 @@ func (spS *SupplierService) matchingSupplierProfilesForEvent(ev *utils.CGREvent, if singleResult { matchingSLP = make([]*SupplierProfile, 1) } - evNm := config.NewNavigableMap(nil) - evNm.Set([]string{utils.MetaReq}, ev.Event, false, false) + evNm := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: ev.Event}) for lpID := range sPrflIDs { splPrfl, err := spS.dm.GetSupplierProfile(ev.Tenant, lpID, true, true, utils.NonTransactional) if err != nil { diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index da4e4dc2e..13c6302f7 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -147,8 +147,8 @@ func (srvMngr *ServiceManager) GetConfig() *config.CGRConfig { func (srvMngr *ServiceManager) StartServices() (err error) { go srvMngr.handleReload() for serviceName, shouldRun := range map[string]bool{ - utils.APIerSv1: srvMngr.GetConfig().ApierCfg().Enabled, - utils.APIerSv2: srvMngr.GetConfig().ApierCfg().Enabled, + utils.APIerSv1: srvMngr.GetConfig().ApierCfg().Enabled, + utils.APIerSv2: srvMngr.GetConfig().ApierCfg().Enabled, utils.StorDB: srvMngr.GetConfig().RalsCfg().Enabled || srvMngr.GetConfig().CdrsCfg().Enabled, utils.AttributeS: srvMngr.GetConfig().AttributeSCfg().Enabled, utils.ChargerS: srvMngr.GetConfig().ChargerSCfg().Enabled, diff --git a/sessions/libsessions.go b/sessions/libsessions.go index b88061048..21eb7c124 100644 --- a/sessions/libsessions.go +++ b/sessions/libsessions.go @@ -48,6 +48,7 @@ type BiRPClient interface { // getSessionTTL retrieves SessionTTL setting out of ev // if SessionTTLMaxDelay is present in ev, the return is randomized +// ToDo: remove if not needed func getSessionTTL(ev *engine.MapEvent, cfgSessionTTL time.Duration, cfgSessionTTLMaxDelay *time.Duration) (ttl time.Duration, err error) { if ttl, err = ev.GetDuration(utils.SessionTTL); err != nil { diff --git a/sessions/sessions.go b/sessions/sessions.go index 293b8120d..bde37968b 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -293,7 +293,7 @@ func (sS *SessionS) setSTerminator(s *Session) { s.sTerminator.timer.Stop() } }() - time.Sleep(1) // force context switch to fix process message + time.Sleep(1) // force context switching } // forceSTerminate is called when a session times-out or it is forced from CGRateS side diff --git a/utils/consts.go b/utils/consts.go index df676bd51..823cd4284 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -490,6 +490,13 @@ const ( Categories = "Categories" Blocker = "Blocker" RatingPlanID = "RatingPlanID" + StartTime = "StartTime" + AccountSummary = "AccountSummary" + RatingFilters = "RatingFilters" + RatingFilter = "RatingFilter" + Accounting = "Accounting" + Rating = "Rating" + Charges = "Charges" MetaSessionS = "*sessions" MetaDefault = "*default" Error = "Error" @@ -1181,7 +1188,7 @@ const ( APIerSv1GetAttributeProfile = "APIerSv1.GetAttributeProfile" APIerSv1GetAttributeProfileIDs = "APIerSv1.GetAttributeProfileIDs" APIerSv1RemoveAttributeProfile = "APIerSv1.RemoveAttributeProfile" - APIerSv2SetAttributeProfile = "APIerSv2.SetAttributeProfile" + APIerSv2SetAttributeProfile = "APIerSv2.SetAttributeProfile" AttributeSv1GetAttributeForEvent = "AttributeSv1.GetAttributeForEvent" AttributeSv1ProcessEvent = "AttributeSv1.ProcessEvent" AttributeSv1Ping = "AttributeSv1.Ping"