RateS - adding V1CostForEvent API method

This commit is contained in:
DanB
2020-11-15 14:37:37 +01:00
parent d2d1139de0
commit 8940ebd7e8
4 changed files with 43 additions and 229 deletions

View File

@@ -173,6 +173,12 @@ type RateSIncrement struct {
cost *utils.Decimal // unexported total increment cost
}
type RateProfileCost struct {
ID string // RateProfileID
Cost float64
RateSIntervals []*RateSInterval
}
// Sort will sort the IntervalRates from each Rate based on IntervalStart
func (rpp *RateProfile) Sort() {
for _, rate := range rpp.Rates {

View File

@@ -205,9 +205,10 @@ func computeRateSIntervals(rts []*orderedRate, intervalStart, usage time.Duratio
}
isLastIRt := j == len(rt.IntervalRates)-1
if iRt.IntervalStart > iRtUsageSIdx {
break
} else if !isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx {
continue // the rates should be already ordered, break here
break // we are past the start
}
if !isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx {
continue // the next interval changes the rating
}
if !isLastIRt {
iRtUsageEIdx = rt.IntervalRates[j+1].IntervalStart

View File

@@ -71,7 +71,7 @@ func (rS *RateS) Call(serviceMethod string, args interface{}, reply interface{})
}
// matchingRateProfileForEvent returns the matched RateProfile for the given event
func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent, rPfIDs []string) (rtPfl *engine.RateProfile, err error) {
func (rS *RateS) matchingRateProfileForEvent(tnt string, rPfIDs []string, args *ArgsCostForEvent) (rtPfl *engine.RateProfile, err error) {
evNm := utils.MapStorage{
utils.MetaReq: args.CGREvent.Event,
utils.MetaOpts: args.Opts,
@@ -93,10 +93,6 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent,
}
rPfIDs = rPfIDMp.AsSlice()
}
var sTime time.Time
if sTime, err = args.StartTime(rS.cfg.GeneralCfg().DefaultTimezone); err != nil {
return
}
for _, rPfID := range rPfIDs {
var rPf *engine.RateProfile
if rPf, err = rS.dm.GetRateProfile(tnt, rPfID,
@@ -107,8 +103,8 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent,
}
return
}
if rPf.ActivationInterval != nil &&
!rPf.ActivationInterval.IsActiveAtTime(sTime) { // not active
if rPf.ActivationInterval != nil && args.CGREvent.Time != nil &&
!rPf.ActivationInterval.IsActiveAtTime(*args.CGREvent.Time) { // not active
continue
}
var pass bool
@@ -128,15 +124,8 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent,
return
}
type RateProfileCost struct {
Tenant string
ID string
Cost float64
RateSIntervals []*engine.RateSInterval
}
// costForEvent computes the cost for an event based on a preselected rating profile
func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *ArgsCostForEvent) (rpCost *RateProfileCost, err error) {
func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *ArgsCostForEvent) (rpCost *engine.RateProfileCost, err error) {
var rtIDs utils.StringSet
if rtIDs, err = engine.MatchingItemIDsForEvent(
args.CGREventWithOpts.CGREvent.Event,
@@ -175,7 +164,7 @@ func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *ArgsCo
if ordRts, err = orderRatesOnIntervals(aRates, sTime, usage, true, 1000000); err != nil {
return
}
rpCost = &RateProfileCost{Tenant: rtPfl.Tenant, ID: rtPfl.ID}
rpCost = &engine.RateProfileCost{ID: rtPfl.ID}
if rpCost.RateSIntervals, err = computeRateSIntervals(ordRts, 0, usage); err != nil {
return nil, err
}
@@ -194,22 +183,6 @@ func (args *ArgsCostForEvent) StartTime(tmz string) (sTime time.Time, err error)
if tIface, has := args.Opts[utils.OptsRatesStartTime]; has {
return utils.IfaceAsTime(tIface, tmz)
}
if sTime, err = args.CGREvent.FieldAsTime(utils.AnswerTime, tmz); err != nil {
if err != utils.ErrNotFound {
return
}
// not found, try SetupTime
if sTime, err = args.CGREvent.FieldAsTime(utils.SetupTime, tmz); err != nil &&
err != utils.ErrNotFound {
return
}
}
if err == nil {
return
}
if args.CGREvent.Time != nil {
return *args.CGREvent.Time, nil
}
return time.Now(), nil
}
@@ -218,15 +191,23 @@ func (args *ArgsCostForEvent) Usage() (usage time.Duration, err error) {
if uIface, has := args.Opts[utils.OptsRatesUsage]; has {
return utils.IfaceAsDuration(uIface)
}
if usage, err = args.CGREvent.FieldAsDuration(utils.Usage); err != nil {
if err != utils.ErrNotFound {
return
}
}
return time.Duration(time.Minute), nil
}
// V1CostForEvent will be called to calculate the cost for an event
func (rS *RateS) V1CostForEvent(args *ArgsCostForEvent, cC *utils.ChargedCost) (err error) {
func (rS *RateS) V1CostForEvent(args *ArgsCostForEvent, rpCost *engine.RateProfileCost) (err error) {
rPfIDs := make([]string, len(args.RateProfileIDs))
for i, rpID := range args.RateProfileIDs {
rPfIDs[i] = rpID
}
var rtPrl *engine.RateProfile
if rtPrl, err = rS.matchingRateProfileForEvent(args.CGREventWithOpts.Tenant, rPfIDs, args); err != nil {
return utils.NewErrServerError(err)
}
if rcvCost, errCost := rS.rateProfileCostForEvent(rtPrl, args); errCost != nil {
return utils.NewErrServerError(errCost)
} else {
*rpCost = *rcvCost
}
return
}

View File

@@ -61,137 +61,6 @@ func TestNewRateS(t *testing.T) {
}
}
func TestStartTime(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Time: utils.TimePointer(time.Date(2020, 12, 24, 0, 0, 0, 0, time.UTC)),
Event: map[string]interface{}{
utils.ToR: utils.SMS,
},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
expectedTime := time.Date(2020, 12, 24, 0, 0, 0, 0, time.UTC)
if sTime, err := argsCost.StartTime(utils.EmptyString); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expectedTime, sTime) {
t.Errorf("Expected %+v, received %+v", expectedTime, sTime)
}
}
func TestStartTimeError(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Time: utils.TimePointer(time.Now()),
Event: map[string]interface{}{
utils.ToR: utils.SMS,
},
},
Opts: map[string]interface{}{
utils.OptsRatesStartTime: "invalidStartTime",
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
expectedErr := "Unsupported time format"
if _, err := argsCost.StartTime(utils.EmptyString); err == nil || err.Error() != expectedErr {
t.Errorf("Expected %+v, received %+v", expectedErr, err)
}
}
func TestStartTimeFieldAsTimeErrorAnswerTime(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Time: utils.TimePointer(time.Now()),
Event: map[string]interface{}{
utils.AnswerTime: "0s",
},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
expectedErr := "Unsupported time format"
if _, err := argsCost.StartTime(utils.EmptyString); err == nil || err.Error() != expectedErr {
t.Errorf("Expected %+v, received %+v", expectedErr, err)
}
}
func TestStartTimeFieldAsTimeErrorSetupTime(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Time: utils.TimePointer(time.Now()),
Event: map[string]interface{}{
utils.SetupTime: "1h0m0s",
},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
expectedErr := "Unsupported time format"
if _, err := argsCost.StartTime(utils.EmptyString); err == nil || err.Error() != expectedErr {
t.Errorf("Expected %+v, received %+v", expectedErr, err)
}
}
func TestStartTimeFieldAsTimeNilTime(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Event: map[string]interface{}{
utils.AnswerTime: time.Time{},
},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
if _, err := argsCost.StartTime(utils.EmptyString); err != nil {
t.Error(err)
}
}
func TestStartTimeFieldAsTimeNow(t *testing.T) {
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Event: map[string]interface{}{},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
if _, err := argsCost.StartTime(utils.EmptyString); err != nil {
t.Error(err)
}
}
func TestCallRates(t *testing.T) {
newRates := &RateS{}
var reply *string
@@ -201,25 +70,6 @@ func TestCallRates(t *testing.T) {
}
}
func TestV1CostForEvent(t *testing.T) {
rts := &RateS{}
cgrEvent := &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "randomID",
Event: map[string]interface{}{},
},
}
rateProfileIDs := []string{"randomIDs"}
argsCost := &ArgsCostForEvent{
rateProfileIDs,
cgrEvent,
}
if err := rts.V1CostForEvent(argsCost, nil); err != nil {
t.Error(err)
}
}
func TestMatchingRateProfileEvent(t *testing.T) {
defaultCfg, err := config.NewDefaultCGRConfig()
if err != nil {
@@ -247,7 +97,7 @@ func TestMatchingRateProfileEvent(t *testing.T) {
if err != nil {
t.Error(err)
}
if rtPRf, err := rate.matchingRateProfileForEvent("cgrates.org",
if rtPRf, err := rate.matchingRateProfileForEvent("cgrates.org", []string{},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -260,14 +110,13 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{}); err != nil {
}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rtPRf, rpp) {
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(rpp), utils.ToJSON(rtPRf))
}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -280,11 +129,10 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{}); err != utils.ErrNotFound {
}); err != utils.ErrNotFound {
t.Error(err)
}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -297,11 +145,10 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{}); err != utils.ErrNotFound {
}); err != utils.ErrNotFound {
t.Error(err)
}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -314,12 +161,11 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{}); err != utils.ErrNotFound {
}); err != utils.ErrNotFound {
t.Error(err)
}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{"rp2"},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -332,30 +178,12 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{"rp2"}); err != utils.ErrNotFound {
t.Error(err)
}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "CACHE1",
Event: map[string]interface{}{},
},
Opts: map[string]interface{}{
utils.OptsRatesStartTime: 0,
},
},
},
[]string{"randomProfileID"}); err.Error() != "cannot convert field: 0 to time.Time" {
}); err != utils.ErrNotFound {
t.Error(err)
}
rpp.FilterIDs = []string{"*string:~*req.Account:1001;1002;1003", "*gt:~*req.Cost{*:10"}
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -368,13 +196,12 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{}); err.Error() != "invalid converter terminator in rule: <~*req.Cost{*>" {
}); err.Error() != "invalid converter terminator in rule: <~*req.Cost{*>" {
t.Error(err)
}
rate.dm = nil
if _, err := rate.matchingRateProfileForEvent("cgrates.org",
if _, err := rate.matchingRateProfileForEvent("cgrates.org", []string{"rp3"},
&ArgsCostForEvent{
CGREventWithOpts: &utils.CGREventWithOpts{
CGREvent: &utils.CGREvent{
@@ -387,8 +214,7 @@ func TestMatchingRateProfileEvent(t *testing.T) {
},
},
},
},
[]string{"rp3"}); err != utils.ErrNoDatabaseConn {
}); err != utils.ErrNoDatabaseConn {
t.Error(err)
}
}