From dea8810abce1bd20b74779825b29e53e91a4de80 Mon Sep 17 00:00:00 2001 From: TeoV Date: Tue, 24 Jul 2018 07:03:45 -0400 Subject: [PATCH] AttributeProfile add Blocker for multiple runs and add tests --- data/tariffplans/oldtutorial/Attributes.csv | 6 +- data/tariffplans/testit/Attributes.csv | 4 +- data/tariffplans/testtp/Attributes.csv | 6 +- data/tariffplans/tutorial/Attributes.csv | 26 +-- dispatcher/dispatcher.go | 1 - engine/attributes.go | 10 +- engine/attributes_test.go | 229 ++++++++++++++++++++ engine/libattributes.go | 1 + engine/loader_csv_test.go | 11 +- engine/model_helpers.go | 9 +- engine/models.go | 3 +- utils/apitpdata.go | 1 + 12 files changed, 276 insertions(+), 31 deletions(-) diff --git a/data/tariffplans/oldtutorial/Attributes.csv b/data/tariffplans/oldtutorial/Attributes.csv index a4dea7fc8..4bb9f90c7 100644 --- a/data/tariffplans/oldtutorial/Attributes.csv +++ b/data/tariffplans/oldtutorial/Attributes.csv @@ -1,3 +1,3 @@ -#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Weight -cgrates.org,ATTR_1,*sessions;*cdrs,*string:Account:1007,2014-01-14T00:00:00Z,Account,*any,1001,false,10 -cgrates.org,ATTR_1,,,,Subject,*any,1001,true, +#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight +cgrates.org,ATTR_1,*sessions;*cdrs,*string:Account:1007,2014-01-14T00:00:00Z,Account,*any,1001,false,false,10 +cgrates.org,ATTR_1,,,,Subject,*any,1001,true,, diff --git a/data/tariffplans/testit/Attributes.csv b/data/tariffplans/testit/Attributes.csv index 26c6383cb..4070be620 100644 --- a/data/tariffplans/testit/Attributes.csv +++ b/data/tariffplans/testit/Attributes.csv @@ -1,2 +1,2 @@ -#Tenant,ID,Context,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Weight -cgrates.org,ATTR_ACNT_1001,*sessions,FLTR_ACCOUNT_1001,,OfficeGroup,*any,Marketing,true,10 +#Tenant,ID,Context,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight +cgrates.org,ATTR_ACNT_1001,*sessions,FLTR_ACCOUNT_1001,,OfficeGroup,*any,Marketing,true,false,10 diff --git a/data/tariffplans/testtp/Attributes.csv b/data/tariffplans/testtp/Attributes.csv index fa8b32c23..c7fc49204 100644 --- a/data/tariffplans/testtp/Attributes.csv +++ b/data/tariffplans/testtp/Attributes.csv @@ -1,3 +1,3 @@ -#,Tenant,ID,Context,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Weight -cgrates.org,ALS1,con1,FLTR_1,2014-07-29T15:00:00Z,Field1,Initial1,Sub1,true,20 -cgrates.org,ALS1,,,,Field2,Initial2,Sub2,false, +#,Tenant,ID,Context,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight +cgrates.org,ALS1,con1,FLTR_1,2014-07-29T15:00:00Z,Field1,Initial1,Sub1,true,false,20 +cgrates.org,ALS1,,,,Field2,Initial2,Sub2,false,, diff --git a/data/tariffplans/tutorial/Attributes.csv b/data/tariffplans/tutorial/Attributes.csv index 40b2c1542..993f10433 100644 --- a/data/tariffplans/tutorial/Attributes.csv +++ b/data/tariffplans/tutorial/Attributes.csv @@ -1,15 +1,15 @@ -#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Weight -cgrates.org,ATTR_1001_SIMPLEAUTH,simpleauth,*string:Account:1001,,Password,*any,CGRateS.org,true,20 -cgrates.org,ATTR_1002_SIMPLEAUTH,simpleauth,*string:Account:1002,,Password,*any,CGRateS.org,true,20 -cgrates.org,ATTR_1003_SIMPLEAUTH,simpleauth,*string:Account:1003,,Password,*any,CGRateS.org,true,20 -cgrates.org,ATTR_1001_SESSIONAUTH,*sessions,*string:Account:1001,,Password,*any,CGRateS.org,true,10 -cgrates.org,ATTR_1001_SESSIONAUTH,,,,RequestType,*any,*prepaid,true, -cgrates.org,ATTR_1001_SESSIONAUTH,,,,PaypalAccount,*any,cgrates@paypal.com,true, -cgrates.org,ATTR_1001_SESSIONAUTH,,,,LCRProfile,*any,premium_cli,true, -cgrates.org,ATTR_1002_SESSIONAUTH,*sessions,*string:Account:1002,,Password,*any,CGRateS.org,true,10 -cgrates.org,ATTR_1002_SESSIONAUTH,,,,RequestType,*any,*postpaid,true, -cgrates.org,ATTR_1002_SESSIONAUTH,,,,PaypalAccount,*any,cgrates@paypal.com,true, -cgrates.org,ATTR_1002_SESSIONAUTH,,,,LCRProfile,*any,premium_cli,true, -cgrates.org,ATTR_1002_SESSIONAUTH,,,,ResourceAllocation,*any,"ResGroup1",true, +#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight +cgrates.org,ATTR_1001_SIMPLEAUTH,simpleauth,*string:Account:1001,,Password,*any,CGRateS.org,true,false,20 +cgrates.org,ATTR_1002_SIMPLEAUTH,simpleauth,*string:Account:1002,,Password,*any,CGRateS.org,true,false,20 +cgrates.org,ATTR_1003_SIMPLEAUTH,simpleauth,*string:Account:1003,,Password,*any,CGRateS.org,true,false,20 +cgrates.org,ATTR_1001_SESSIONAUTH,*sessions,*string:Account:1001,,Password,*any,CGRateS.org,true,false,10 +cgrates.org,ATTR_1001_SESSIONAUTH,,,,RequestType,*any,*prepaid,true,, +cgrates.org,ATTR_1001_SESSIONAUTH,,,,PaypalAccount,*any,cgrates@paypal.com,true,, +cgrates.org,ATTR_1001_SESSIONAUTH,,,,LCRProfile,*any,premium_cli,true,, +cgrates.org,ATTR_1002_SESSIONAUTH,*sessions,*string:Account:1002,,Password,*any,CGRateS.org,true,false,10 +cgrates.org,ATTR_1002_SESSIONAUTH,,,,RequestType,*any,*postpaid,true,, +cgrates.org,ATTR_1002_SESSIONAUTH,,,,PaypalAccount,*any,cgrates@paypal.com,true,, +cgrates.org,ATTR_1002_SESSIONAUTH,,,,LCRProfile,*any,premium_cli,true,, +cgrates.org,ATTR_1002_SESSIONAUTH,,,,ResourceAllocation,*any,"ResGroup1",true,, diff --git a/dispatcher/dispatcher.go b/dispatcher/dispatcher.go index 064bcae54..bb9206b59 100755 --- a/dispatcher/dispatcher.go +++ b/dispatcher/dispatcher.go @@ -125,7 +125,6 @@ func (dS *DispatcherService) authorize(method, tenant, apiKey string, evTime *ti return } var apiMethods string - fmt.Printf("\nRPL EV : %+v\n", utils.ToJSON(rplyEv)) if apiMethods, err = rplyEv.CGREvent.FieldAsString(utils.APIMethods); err != nil { return } diff --git a/engine/attributes.go b/engine/attributes.go index 9e1cfb1cb..7f1d9d114 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -141,6 +141,7 @@ type AttrSProcessEventReply struct { MatchedProfiles []string AlteredFields []string CGREvent *utils.CGREvent + blocker bool } // Digest returns serialized version of alteredFields in AttrSProcessEventReply @@ -174,7 +175,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( } rply = &AttrSProcessEventReply{ MatchedProfiles: []string{attrPrf.ID}, - CGREvent: args.Clone()} + CGREvent: args.Clone(), + blocker: attrPrf.Blocker} for fldName, initialMp := range attrPrf.attributesIdx { initEvValIf, has := args.Event[fldName] if !has { @@ -255,6 +257,9 @@ func (alS *AttributeService) V1ProcessEvent(args *AttrArgsProcessEvent, } if apiRply == nil { // first reply apiRply = evRply + if apiRply.blocker == true { + break + } continue } if utils.IsSliceMember(apiRply.MatchedProfiles, @@ -269,6 +274,9 @@ func (alS *AttributeService) V1ProcessEvent(args *AttrArgsProcessEvent, } apiRply.AlteredFields = append(apiRply.AlteredFields, fldName) } + if evRply.blocker == true { + break + } } *reply = *apiRply return diff --git a/engine/attributes_test.go b/engine/attributes_test.go index 2c5eef9bf..8175c8df6 100644 --- a/engine/attributes_test.go +++ b/engine/attributes_test.go @@ -966,3 +966,232 @@ func TestAttributeProcessWithMultipleRuns4(t *testing.T) { t.Errorf("Expecting %+v, received: %+v", eRply.CGREvent.Event, reply.CGREvent.Event) } } + +func TestAttributeMultipleProcessWithBlocker(t *testing.T) { + //refresh the DM + if err := dmAtr.DataDB().Flush(""); err != nil { + t.Error(err) + } + if test, err := dmAtr.DataDB().IsDBEmpty(); err != nil { + t.Error(err) + } else if test != true { + t.Errorf("\nExpecting: true got :%+v", test) + } + attrPrf1 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_1", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:InitialField:InitialValue"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field1", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value1", true), + Append: true, + }, + }, + Weight: 10, + } + attrPrf2 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_2", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:Field1:Value1"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field2", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value2", true), + Append: true, + }, + }, + Blocker: true, + Weight: 20, + } + attrPrf3 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_3", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:Field2:Value2"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field3", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value3", true), + Append: true, + }, + }, + Weight: 30, + } + // Add attribute in DM + if err := dmAtr.SetAttributeProfile(attrPrf1, true); err != nil { + t.Error(err) + } + if err = dmAtr.SetAttributeProfile(attrPrf2, true); err != nil { + t.Errorf("Error: %+v", err) + } + if err = dmAtr.SetAttributeProfile(attrPrf3, true); err != nil { + t.Errorf("Error: %+v", err) + } + attrArgs := &AttrArgsProcessEvent{ + ProcessRuns: utils.IntPointer(4), + CGREvent: utils.CGREvent{ + Tenant: config.CgrConfig().DefaultTenant, + ID: utils.GenUUID(), + Context: utils.StringPointer(utils.MetaSessionS), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + }, + }, + } + eRply := &AttrSProcessEventReply{ + MatchedProfiles: []string{"ATTR_1", "ATTR_2"}, + AlteredFields: []string{"Field1", "Field2"}, + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().DefaultTenant, + ID: utils.GenUUID(), + Context: utils.StringPointer(utils.MetaSessionS), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + "Field1": "Value1", + "Field2": "Value2", + }, + }, + } + var reply AttrSProcessEventReply + if err := attrService.V1ProcessEvent(attrArgs, &reply); err != nil { + t.Errorf("Error: %+v", err) + } + if !reflect.DeepEqual(eRply.MatchedProfiles, reply.MatchedProfiles) { + t.Errorf("Expecting %+v, received: %+v", eRply.MatchedProfiles, reply.MatchedProfiles) + } + if !reflect.DeepEqual(eRply.AlteredFields, reply.AlteredFields) { + t.Errorf("Expecting %+v, received: %+v", eRply.AlteredFields, reply.AlteredFields) + } + if !reflect.DeepEqual(eRply.CGREvent.Event, reply.CGREvent.Event) { + t.Errorf("Expecting %+v, received: %+v", eRply.CGREvent.Event, reply.CGREvent.Event) + } +} + +func TestAttributeMultipleProcessWithBlocker2(t *testing.T) { + //refresh the DM + if err := dmAtr.DataDB().Flush(""); err != nil { + t.Error(err) + } + if test, err := dmAtr.DataDB().IsDBEmpty(); err != nil { + t.Error(err) + } else if test != true { + t.Errorf("\nExpecting: true got :%+v", test) + } + attrPrf1 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_1", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:InitialField:InitialValue"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field1", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value1", true), + Append: true, + }, + }, + Blocker: true, + Weight: 10, + } + attrPrf2 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_2", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:Field1:Value1"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field2", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value2", true), + Append: true, + }, + }, + Weight: 20, + } + attrPrf3 := &AttributeProfile{ + Tenant: config.CgrConfig().DefaultTenant, + ID: "ATTR_3", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:Field2:Value2"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "Field3", + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("Value3", true), + Append: true, + }, + }, + Weight: 30, + } + // Add attribute in DM + if err := dmAtr.SetAttributeProfile(attrPrf1, true); err != nil { + t.Error(err) + } + if err = dmAtr.SetAttributeProfile(attrPrf2, true); err != nil { + t.Errorf("Error: %+v", err) + } + if err = dmAtr.SetAttributeProfile(attrPrf3, true); err != nil { + t.Errorf("Error: %+v", err) + } + attrArgs := &AttrArgsProcessEvent{ + ProcessRuns: utils.IntPointer(4), + CGREvent: utils.CGREvent{ + Tenant: config.CgrConfig().DefaultTenant, + ID: utils.GenUUID(), + Context: utils.StringPointer(utils.MetaSessionS), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + }, + }, + } + eRply := &AttrSProcessEventReply{ + MatchedProfiles: []string{"ATTR_1"}, + AlteredFields: []string{"Field1"}, + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().DefaultTenant, + ID: utils.GenUUID(), + Context: utils.StringPointer(utils.MetaSessionS), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + "Field1": "Value1", + }, + }, + } + var reply AttrSProcessEventReply + if err := attrService.V1ProcessEvent(attrArgs, &reply); err != nil { + t.Errorf("Error: %+v", err) + } + if !reflect.DeepEqual(eRply.MatchedProfiles, reply.MatchedProfiles) { + t.Errorf("Expecting %+v, received: %+v", eRply.MatchedProfiles, reply.MatchedProfiles) + } + if !reflect.DeepEqual(eRply.AlteredFields, reply.AlteredFields) { + t.Errorf("Expecting %+v, received: %+v", eRply.AlteredFields, reply.AlteredFields) + } + if !reflect.DeepEqual(eRply.CGREvent.Event, reply.CGREvent.Event) { + t.Errorf("Expecting %+v, received: %+v", eRply.CGREvent.Event, reply.CGREvent.Event) + } +} diff --git a/engine/libattributes.go b/engine/libattributes.go index cb3c4485e..741c9c17c 100644 --- a/engine/libattributes.go +++ b/engine/libattributes.go @@ -38,6 +38,7 @@ type AttributeProfile struct { FilterIDs []string ActivationInterval *utils.ActivationInterval // Activation interval Attributes []*Attribute + Blocker bool // blocker flag to stop processing on multiple runs Weight float64 attributesIdx map[string]map[interface{}]*Attribute // map[FieldName][InitialValue]*Attribute, used as event match index diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 743c32e04..f09365d76 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -299,9 +299,9 @@ cgrates.org,SPP_1,,,,,supplier1,FLTR_DST_DE,Account2,RPL_3,ResGroup3,Stat2,10,,, cgrates.org,SPP_1,,,,,supplier1,,,,ResGroup4,Stat3,10,,, ` attributeProfiles = ` -#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Weight -cgrates.org,ALS1,con1,FLTR_1,2014-07-29T15:00:00Z,Field1,Initial1,Sub1,true,20 -cgrates.org,ALS1,con2;con3,,,Field2,Initial2,Sub2,false, +#Tenant,ID,Contexts,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight +cgrates.org,ALS1,con1,FLTR_1,2014-07-29T15:00:00Z,Field1,Initial1,Sub1,true,true,20 +cgrates.org,ALS1,con2;con3,,,Field2,Initial2,Sub2,false,, ` chargerProfiles = ` #Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight @@ -1771,7 +1771,8 @@ func TestLoadAttributeProfiles(t *testing.T) { Append: false, }, }, - Weight: 20, + Blocker: true, + Weight: 20, }, } resKey := utils.TenantID{Tenant: "cgrates.org", ID: "ALS1"} @@ -1789,6 +1790,8 @@ func TestLoadAttributeProfiles(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eAttrProfiles[resKey].ActivationInterval, csvr.attributeProfiles[resKey].ActivationInterval) } else if !reflect.DeepEqual(eAttrProfiles[resKey].Attributes, csvr.attributeProfiles[resKey].Attributes) { t.Errorf("Expecting: %+v, received: %+v", eAttrProfiles[resKey].Attributes, csvr.attributeProfiles[resKey].Attributes) + } else if !reflect.DeepEqual(eAttrProfiles[resKey].Blocker, csvr.attributeProfiles[resKey].Blocker) { + t.Errorf("Expecting: %+v, received: %+v", eAttrProfiles[resKey].Blocker, csvr.attributeProfiles[resKey].Blocker) } } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 5af683c90..07341e93e 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -2684,9 +2684,10 @@ func (tps TPAttributes) AsTPAttributes() (result []*utils.TPAttributeProfile) { th, found := mst[(&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID()] if !found { th = &utils.TPAttributeProfile{ - TPid: tp.Tpid, - Tenant: tp.Tenant, - ID: tp.ID, + TPid: tp.Tpid, + Tenant: tp.Tenant, + ID: tp.ID, + Blocker: tp.Blocker, } } if tp.Weight != 0 { @@ -2756,6 +2757,7 @@ func APItoModelTPAttribute(th *utils.TPAttributeProfile) (mdls TPAttributes) { ID: th.ID, } if i == 0 { + mdl.Blocker = th.Blocker if th.ActivationInterval != nil { if th.ActivationInterval.ActivationTime != "" { mdl.ActivationInterval = th.ActivationInterval.ActivationTime @@ -2794,6 +2796,7 @@ func APItoAttributeProfile(tpTH *utils.TPAttributeProfile, timezone string) (th Tenant: tpTH.Tenant, ID: tpTH.ID, Weight: tpTH.Weight, + Blocker: tpTH.Blocker, FilterIDs: []string{}, Contexts: []string{}, } diff --git a/engine/models.go b/engine/models.go index 11e7e5114..ab0120ad9 100644 --- a/engine/models.go +++ b/engine/models.go @@ -555,7 +555,8 @@ type TPAttribute struct { Initial string `index:"6" re:""` Substitute string `index:"7" re:""` Append bool `index:"8" re:""` - Weight float64 `index:"9" re:"\d+\.?\d*"` + Blocker bool `index:"9" re:""` + Weight float64 `index:"10" re:"\d+\.?\d*"` CreatedAt time.Time } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 4a4dcdf98..16f36d64e 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1423,6 +1423,7 @@ type TPAttributeProfile struct { ActivationInterval *TPActivationInterval // Time when this limit becomes active and expires Contexts []string // bind this TPAttribute to multiple context Attributes []*TPAttribute + Blocker bool Weight float64 }