From b2e9f5c181e97f2ea0ddf5e7c7a4559a6f557588 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 31 Mar 2018 19:44:41 +0200 Subject: [PATCH] Improvements on Suppliers.MaxCost=*event_cost computation --- apier/v1/suppliers_it_test.go | 2 +- engine/calldesc.go | 46 ++++++++++++ engine/suppliers.go | 131 ++++++++-------------------------- sessions/sessions.go | 1 - 4 files changed, 75 insertions(+), 105 deletions(-) diff --git a/apier/v1/suppliers_it_test.go b/apier/v1/suppliers_it_test.go index 5405fc339..f9780e427 100644 --- a/apier/v1/suppliers_it_test.go +++ b/apier/v1/suppliers_it_test.go @@ -284,7 +284,7 @@ func testV1SplSGetLeastCostSuppliersWithMaxCostNotFound(t *testing.T) { } var suplsReply engine.SortedSuppliers if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers, - ev, &suplsReply); err.Error() != utils.ErrNotFound.Error() { + ev, &suplsReply); err != nil && err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } diff --git a/engine/calldesc.go b/engine/calldesc.go index 3500fbef0..8da738463 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -113,6 +113,52 @@ func Publish(event CgrEvent) { } } +// NewCallDescriptorFromCGREvent converts a CGREvent into CallDescriptor +func NewCallDescriptorFromCGREvent(cgrEv *utils.CGREvent, + timezone string) (cd *CallDescriptor, err error) { + cd = &CallDescriptor{Direction: utils.OUT, Tenant: cgrEv.Tenant} + if _, has := cgrEv.Event[utils.Category]; has { + if cd.Category, err = cgrEv.FieldAsString(utils.Category); err != nil { + return nil, err + } + } + if cd.Account, err = cgrEv.FieldAsString(utils.Account); err != nil { + return + } + if cd.Subject, err = cgrEv.FieldAsString(utils.Subject); err != nil { + if err != utils.ErrNotFound { + return + } + cd.Subject = cd.Account + } + if cd.Destination, err = cgrEv.FieldAsString(utils.Destination); err != nil { + return nil, err + } + if cd.TimeStart, err = cgrEv.FieldAsTime(utils.SetupTime, + timezone); err != nil { + return nil, err + } + if _, has := cgrEv.Event[utils.AnswerTime]; has { // AnswerTime takes precendence for TimeStart + if aTime, err := cgrEv.FieldAsTime(utils.AnswerTime, + timezone); err != nil { + return nil, err + } else if !aTime.IsZero() { + cd.TimeStart = aTime + } + } + if usage, err := cgrEv.FieldAsDuration(utils.Usage); err != nil { + return nil, err + } else { + cd.TimeEnd = cd.TimeStart.Add(usage) + } + if _, has := cgrEv.Event[utils.TOR]; has { + if cd.TOR, err = cgrEv.FieldAsString(utils.TOR); err != nil { + return nil, err + } + } + return +} + /* The input stucture that contains call information. */ diff --git a/engine/suppliers.go b/engine/suppliers.go index 549337d3f..687e846f6 100644 --- a/engine/suppliers.go +++ b/engine/suppliers.go @@ -180,11 +180,8 @@ func (spS *SupplierService) costForEvent(ev *utils.CGREvent, if sTime, err = ev.FieldAsTime(utils.SetupTime, spS.timezone); err != nil { return } - usage := time.Duration(time.Minute) - if _, has := ev.Event[utils.Usage]; has { - if usage, err = ev.FieldAsDuration(utils.Usage); err != nil { - return - } + if usage, err = ev.FieldAsDuration(utils.Usage); err != nil { + return } for _, anctID := range acntIDs { cd := &CallDescriptor{ @@ -265,6 +262,9 @@ func (spS *SupplierService) resourceUsage(resIDs []string) (tUsage float64, err // supliersForEvent will return the list of valid supplier IDs // for event based on filters and sorting algorithms func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) { + if _, has := args.CGREvent.Event[utils.Usage]; !has { + args.CGREvent.Event[utils.Usage] = time.Duration(time.Minute) // make sure we have default set for Usage + } var suppPrfls SupplierProfiles if suppPrfls, err = spS.matchingSupplierProfilesForEvent(&args.CGREvent); err != nil { return @@ -284,11 +284,12 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor } spls = append(spls, s) } - extraOpts, err := args.asOptsGetSuppliers(spls) + extraOpts, err := args.asOptsGetSuppliers(spls) // convert suppliers arguments into internal options used to limit data if err != nil { return nil, err } - sortedSuppliers, err := spS.sorter.SortSuppliers(splPrfl.ID, splPrfl.Sorting, spls, &args.CGREvent, extraOpts) + sortedSuppliers, err := spS.sorter.SortSuppliers(splPrfl.ID, splPrfl.Sorting, + spls, &args.CGREvent, extraOpts) if err != nil { return nil, err } @@ -307,29 +308,33 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor type ArgsGetSuppliers struct { IgnoreErrors bool - MaxCost string + MaxCost string // toDo: try with interface{} here utils.CGREvent utils.Paginator } -func (args *ArgsGetSuppliers) asOptsGetSuppliers(supp []*Supplier) (out *optsGetSuppliers, err error) { - out = new(optsGetSuppliers) - if args.MaxCost != "" { - if args.MaxCost == utils.MetaEventCost { - val, err := out.costForEvent(&args.CGREvent, supp) - if err != nil { - return nil, err - } - out.maxCost = val +func (args *ArgsGetSuppliers) asOptsGetSuppliers(supp []*Supplier) (opts *optsGetSuppliers, err error) { + opts = &optsGetSuppliers{ignoreErrors: args.IgnoreErrors} + if args.MaxCost == utils.MetaEventCost { // dynamic cost needs to be calculated from event + if err = args.CGREvent.CheckMandatoryFields([]string{utils.Account, + utils.Destination, utils.SetupTime, utils.Usage}); err != nil { + return + } + cd, err := NewCallDescriptorFromCGREvent(&args.CGREvent, config.CgrConfig().DefaultTimezone) + if err != nil { + return nil, err + } + if cc, err := cd.GetCost(); err != nil { + return nil, err } else { - val, err := strconv.ParseFloat(args.MaxCost, 64) - if err != nil { - return nil, err - } - out.maxCost = val + opts.maxCost = cc.Cost + } + } else if args.MaxCost != "" { + if opts.maxCost, err = strconv.ParseFloat(args.MaxCost, + 64); err != nil { + return nil, err } } - out.ignoreErrors = args.IgnoreErrors return } @@ -338,86 +343,6 @@ type optsGetSuppliers struct { maxCost float64 } -func (args *optsGetSuppliers) costForEvent(ev *utils.CGREvent, suppls []*Supplier) (val float64, err error) { - if err = ev.CheckMandatoryFields([]string{utils.Account, - utils.Destination, utils.SetupTime}); err != nil { - return - } - var acnt, subj, dst, tenant string - if acnt, err = ev.FieldAsString(utils.Account); err != nil { - return - } - if subj, err = ev.FieldAsString(utils.Account); err != nil { - if err != utils.ErrNotFound { - return - } - subj = acnt - } - if dst, err = ev.FieldAsString(utils.Destination); err != nil { - return - } - if tenant, err = ev.FieldAsString(utils.Tenant); err != nil { - if err != utils.ErrNotFound { - return - } - tenant = config.CgrConfig().DefaultTenant - } - var sTime time.Time - if sTime, err = ev.FieldAsTime(utils.SetupTime, config.CgrConfig().DefaultTimezone); err != nil { - return - } - usage := time.Duration(time.Minute) - if _, has := ev.Event[utils.Usage]; has { - if usage, err = ev.FieldAsDuration(utils.Usage); err != nil { - return - } - } - // not sure here how to do or from where get ratingPlan for cost - maxCost := -1.0 - for _, s := range suppls { - for _, rp := range s.RatingPlanIDs { // loop through RatingPlans until we find one without errors - rPrfl := &RatingProfile{ - Id: utils.ConcatenatedKey(utils.OUT, - tenant, utils.MetaSuppliers, subj), - RatingPlanActivations: RatingPlanActivations{ - &RatingPlanActivation{ - ActivationTime: sTime, - RatingPlanId: rp, - }, - }, - } - // force cache set so it can be picked by calldescriptor for cost calculation - Cache.Set(utils.CacheRatingProfiles, rPrfl.Id, rPrfl, nil, - true, utils.NonTransactional) - cd := &CallDescriptor{ - Direction: utils.OUT, - Category: utils.MetaSuppliers, - Tenant: tenant, - Subject: subj, - Account: acnt, - Destination: dst, - TimeStart: sTime, - TimeEnd: sTime.Add(usage), - DurationIndex: usage, - } - cc, err := cd.GetCost() - Cache.Remove(utils.CacheRatingProfiles, rPrfl.Id, - true, utils.NonTransactional) // Remove here so we don't overload memory - if err != nil { - if err != utils.ErrNotFound { - return 0.0, err - } - continue - } - ec := NewEventCostFromCallCost(cc, "", "") - if ec.GetCost() > maxCost { - maxCost = ec.GetCost() - } - } - } - return maxCost, nil -} - // V1GetSuppliersForEvent returns the list of valid supplier IDs func (spS *SupplierService) V1GetSuppliers(args *ArgsGetSuppliers, reply *SortedSuppliers) (err error) { if missing := utils.MissingStructFields(&args.CGREvent, []string{"Tenant", "ID"}); len(missing) != 0 { diff --git a/sessions/sessions.go b/sessions/sessions.go index fb417a3f4..98bca025e 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -1406,7 +1406,6 @@ func (smg *SMGeneric) BiRPCv1AuthorizeEvent(clnt rpcclient.RpcClientConnection, return utils.NewErrNotConnected(utils.SupplierS) } cgrEv := args.CGREvent.Clone() - cgrEv.Event[utils.Usage] = time.Duration(time.Minute) if acd, has := cgrEv.Event[utils.ACD]; has { cgrEv.Event[utils.Usage] = acd }