diff --git a/engine/attributes.go b/engine/attributes.go index 2233155d9..220868663 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -168,15 +168,18 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( rply = &AttrSProcessEventReply{ MatchedProfiles: []string{attrPrf.ID}, CGREvent: args.Clone()} - for fldName, initialMp := range attrPrf.attributes { + for fldName, initialMp := range attrPrf.attributesIdx { initEvValIf, has := args.Event[fldName] if !has { anyInitial, hasAny := initialMp[utils.ANY] - if hasAny && anyInitial.Append && - initialMp[utils.ANY].Substitute.Id() != utils.META_NONE { - rply.CGREvent.Event[fldName] = anyInitial.Substitute.Id() + if hasAny && anyInitial.Append { // add field name + substitute, err := anyInitial.Substitute.ParseEvent(args.Event) + if err != nil { + return nil, err + } + rply.CGREvent.Event[fldName] = substitute + rply.AlteredFields = append(rply.AlteredFields, fldName) } - rply.AlteredFields = append(rply.AlteredFields, fldName) continue } attrVal, has := initialMp[initEvValIf] @@ -184,10 +187,14 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( attrVal, has = initialMp[utils.ANY] } if has { - if attrVal.Substitute.Id() == utils.META_NONE { + substitute, err := attrVal.Substitute.ParseEvent(args.Event) + if err != nil { + return nil, err + } + if substitute == utils.META_NONE { delete(rply.CGREvent.Event, fldName) } else { - rply.CGREvent.Event[fldName] = attrVal.Substitute.Id() + rply.CGREvent.Event[fldName] = substitute } rply.AlteredFields = append(rply.AlteredFields, fldName) } diff --git a/engine/attributes_test.go b/engine/attributes_test.go index ae8c0e59c..87823195c 100644 --- a/engine/attributes_test.go +++ b/engine/attributes_test.go @@ -36,7 +36,7 @@ var ( utils.META_ANY: &Attribute{ FieldName: utils.Account, Initial: utils.META_ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1010", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1010", true), Append: true, }, }, @@ -101,12 +101,12 @@ var ( &Attribute{ FieldName: utils.Account, Initial: utils.META_ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1010", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1010", true), Append: true, }, }, - Weight: 20, - attributes: mapSubstitutes, + Weight: 20, + attributesIdx: mapSubstitutes, }, &AttributeProfile{ Tenant: config.CgrConfig().DefaultTenant, @@ -121,12 +121,12 @@ var ( &Attribute{ FieldName: utils.Account, Initial: utils.META_ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1010", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1010", true), Append: true, }, }, - Weight: 20, - attributes: mapSubstitutes, + Weight: 20, + attributesIdx: mapSubstitutes, }, &AttributeProfile{ Tenant: config.CgrConfig().DefaultTenant, @@ -141,12 +141,12 @@ var ( &Attribute{ FieldName: utils.Account, Initial: utils.META_ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1010", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1010", true), Append: true, }, }, - attributes: mapSubstitutes, - Weight: 20, + attributesIdx: mapSubstitutes, + Weight: 20, }, &AttributeProfile{ Tenant: config.CgrConfig().DefaultTenant, @@ -161,12 +161,12 @@ var ( &Attribute{ FieldName: utils.Account, Initial: utils.META_ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1010", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1010", true), Append: true, }, }, - attributes: mapSubstitutes, - Weight: 20, + attributesIdx: mapSubstitutes, + Weight: 20, }, } ) @@ -463,20 +463,14 @@ func TestAttributeIndexer(t *testing.T) { }, Attributes: []*Attribute{ &Attribute{ - FieldName: utils.Account, - Initial: utils.META_ANY, - Append: true, + FieldName: utils.Account, + Initial: utils.META_ANY, + Substitute: utils.NewRSRParsersMustCompile("1010", true), + Append: true, }, }, Weight: 20, } - //populate Substitute from attributes - rsrFields, err := utils.ParseRSRFields("^1010", utils.INFIELD_SEP) - if err != nil { - t.Error(err) - } - attrPrf.Attributes[0].Substitute = rsrFields - if err := dmAtr.SetAttributeProfile(attrPrf, true); err != nil { t.Error(err) } diff --git a/engine/datamanager.go b/engine/datamanager.go index aedf27d07..0d77021d1 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -1068,7 +1068,7 @@ func (dm *DataManager) RemoveSupplierProfile(tenant, id, transactionID string, w } func (dm *DataManager) GetAttributeProfile(tenant, id string, skipCache bool, - transactionID string) (alsPrf *AttributeProfile, err error) { + transactionID string) (attrPrfl *AttributeProfile, err error) { tntID := utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := Cache.Get(utils.CacheAttributeProfiles, tntID); ok { @@ -1078,7 +1078,7 @@ func (dm *DataManager) GetAttributeProfile(tenant, id string, skipCache bool, return x.(*AttributeProfile), nil } } - alsPrf, err = dm.dataDB.GetAttributeProfileDrv(tenant, id) + attrPrfl, err = dm.dataDB.GetAttributeProfileDrv(tenant, id) if err != nil { if err == utils.ErrNotFound { Cache.Set(utils.CacheAttributeProfiles, tntID, nil, nil, @@ -1086,17 +1086,10 @@ func (dm *DataManager) GetAttributeProfile(tenant, id string, skipCache bool, } return nil, err } - alsPrf.attributes = make(map[string]map[interface{}]*Attribute) - for _, attr := range alsPrf.Attributes { - alsPrf.attributes[attr.FieldName] = make(map[interface{}]*Attribute) - alsPrf.attributes[attr.FieldName][attr.Initial] = &Attribute{ - FieldName: attr.FieldName, - Initial: attr.Initial, - Substitute: attr.Substitute, - Append: attr.Append, - } + if err = attrPrfl.Compile(); err != nil { + return nil, err } - Cache.Set(utils.CacheAttributeProfiles, tntID, alsPrf, nil, + Cache.Set(utils.CacheAttributeProfiles, tntID, attrPrfl, nil, cacheCommit(transactionID), transactionID) return } diff --git a/engine/libattributes.go b/engine/libattributes.go index 4d33e26e5..cb3c4485e 100644 --- a/engine/libattributes.go +++ b/engine/libattributes.go @@ -27,7 +27,7 @@ import ( type Attribute struct { FieldName string Initial interface{} - Substitute utils.RSRFields + Substitute utils.RSRParsers Append bool } @@ -36,10 +36,37 @@ type AttributeProfile struct { ID string Contexts []string // bind this AttributeProfile to multiple contexts FilterIDs []string - ActivationInterval *utils.ActivationInterval // Activation interval - attributes map[string]map[interface{}]*Attribute // map[FieldName][InitialValue]*Attribute + ActivationInterval *utils.ActivationInterval // Activation interval Attributes []*Attribute Weight float64 + + attributesIdx map[string]map[interface{}]*Attribute // map[FieldName][InitialValue]*Attribute, used as event match index +} + +// computeAttributesIndex populates .attributes +func (ap *AttributeProfile) computeAttributesIndex() { + ap.attributesIdx = make(map[string]map[interface{}]*Attribute) + for _, attr := range ap.Attributes { + if _, has := ap.attributesIdx[attr.FieldName]; !has { + ap.attributesIdx[attr.FieldName] = make(map[interface{}]*Attribute) + } + ap.attributesIdx[attr.FieldName][attr.Initial] = attr + } +} + +func (ap *AttributeProfile) compileSubstitutes() (err error) { + for _, attr := range ap.Attributes { + if err = attr.Substitute.Compile(); err != nil { + return + } + } + return +} + +// Compile is a wrapper for convenience setting up the AttributeProfile +func (ap *AttributeProfile) Compile() error { + ap.computeAttributesIndex() + return ap.compileSubstitutes() } func (als *AttributeProfile) TenantID() string { diff --git a/engine/model_helpers.go b/engine/model_helpers.go index e428c254e..5af683c90 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -2803,31 +2803,17 @@ func APItoAttributeProfile(tpTH *utils.TPAttributeProfile, timezone string) (th for _, context := range tpTH.Contexts { th.Contexts = append(th.Contexts, context) } - th.attributes = make(map[string]map[interface{}]*Attribute) for _, reqAttr := range tpTH.Attributes { - th.Attributes = append(th.Attributes, &Attribute{ - Append: reqAttr.Append, - FieldName: reqAttr.FieldName, - Initial: reqAttr.Initial, - Substitute: utils.RSRFields{ - &utils.RSRField{ - Id: reqAttr.Substitute, - RSRules: []*utils.ReSearchReplace{}, //from mongo we get empty slice and from redis nil - }, - }, - }) - th.attributes[reqAttr.FieldName] = make(map[interface{}]*Attribute) - th.attributes[reqAttr.FieldName][reqAttr.Initial] = &Attribute{ - FieldName: reqAttr.FieldName, - Initial: reqAttr.Initial, - Substitute: utils.RSRFields{ - &utils.RSRField{ - Id: reqAttr.Substitute, - RSRules: []*utils.ReSearchReplace{}, //from mongo we get empty slice and from redis nil - }, - }, - Append: reqAttr.Append, + sbstPrsr, err := utils.NewRSRParsers(reqAttr.Substitute, true) + if err != nil { + return nil, err } + th.Attributes = append(th.Attributes, &Attribute{ + Append: reqAttr.Append, + FieldName: reqAttr.FieldName, + Initial: reqAttr.Initial, + Substitute: sbstPrsr, + }) } if tpTH.ActivationInterval != nil { if th.ActivationInterval, err = tpTH.ActivationInterval.AsActivationInterval(timezone); err != nil { diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 3e066704e..74b2d8d70 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -1475,7 +1475,7 @@ func TestAPItoAttributeProfile(t *testing.T) { mapSubstitutes["FL1"]["In1"] = &Attribute{ FieldName: "FL1", Initial: "In1", - Substitute: utils.RSRFields{&utils.RSRField{Id: "Al1", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("Al1", true), Append: true, } expected := &AttributeProfile{ @@ -1490,12 +1490,11 @@ func TestAPItoAttributeProfile(t *testing.T) { &Attribute{ FieldName: "FL1", Initial: "In1", - Substitute: utils.RSRFields{&utils.RSRField{Id: "Al1", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("Al1", true), Append: true, }, }, - Weight: 20, - attributes: mapSubstitutes, + Weight: 20, } if rcv, err := APItoAttributeProfile(tpAlsPrf, "UTC"); err != nil { t.Error(err) diff --git a/loaders/loader_test.go b/loaders/loader_test.go index 043340648..c31ec66c1 100644 --- a/loaders/loader_test.go +++ b/loaders/loader_test.go @@ -112,13 +112,13 @@ cgrates.org,TestLoader1,lcr,*string:Account:1008;*string:Account:1009,,Subject,* &engine.Attribute{ FieldName: "Account", Initial: utils.ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1001", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1001", true), Append: false, }, &engine.Attribute{ FieldName: "Subject", Initial: utils.ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1001", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1001", true), Append: true, }}, Weight: 10.0, @@ -207,7 +207,7 @@ func TestLoaderProcessContentMultiFiles(t *testing.T) { &engine.Attribute{ FieldName: "Subject", Initial: utils.ANY, - Substitute: utils.RSRFields{&utils.RSRField{Id: "1001", RSRules: []*utils.ReSearchReplace{}}}, + Substitute: utils.NewRSRParsersMustCompile("1001", true), Append: true, }}, Weight: 10.0, diff --git a/migrator/attributes.go b/migrator/attributes.go index 4240f48d6..53b8a7722 100644 --- a/migrator/attributes.go +++ b/migrator/attributes.go @@ -80,7 +80,10 @@ func (m *Migrator) migrateV1Attributes() (err error) { break } if v1Attr != nil { - attrPrf := v1Attr.AsAttributeProfile() + attrPrf, err := v1Attr.AsAttributeProfile() + if err != nil { + return err + } if m.dryRun != true { if err := m.dmOut.DataManager().DataDB().SetAttributeProfileDrv(attrPrf); err != nil { return err @@ -137,7 +140,7 @@ func (m *Migrator) migrateAttributeProfile() (err error) { return } -func (v1AttrPrf v1AttributeProfile) AsAttributeProfile() (attrPrf *engine.AttributeProfile) { +func (v1AttrPrf v1AttributeProfile) AsAttributeProfile() (attrPrf *engine.AttributeProfile, err error) { attrPrf = &engine.AttributeProfile{ Tenant: v1AttrPrf.Tenant, ID: v1AttrPrf.ID, @@ -149,10 +152,14 @@ func (v1AttrPrf v1AttributeProfile) AsAttributeProfile() (attrPrf *engine.Attrib for _, mp := range v1AttrPrf.Attributes { for _, attr := range mp { initIface := utils.StringToInterface(attr.Initial) + sbstPrsr, err := utils.NewRSRParsers(attr.Substitute, true) + if err != nil { + return nil, err + } attrPrf.Attributes = append(attrPrf.Attributes, &engine.Attribute{ FieldName: attr.FieldName, Initial: initIface, - Substitute: utils.RSRFields{&utils.RSRField{Id: attr.Substitute}}, + Substitute: sbstPrsr, Append: attr.Append, }) } diff --git a/migrator/attributes_test.go b/migrator/attributes_test.go index 1b987d454..850752892 100644 --- a/migrator/attributes_test.go +++ b/migrator/attributes_test.go @@ -65,13 +65,15 @@ func Testv1AttributeProfileAsAttributeProfile(t *testing.T) { &engine.Attribute{ FieldName: "FL1", Initial: "In1", - Substitute: utils.RSRFields{&utils.RSRField{Id: "Al1"}}, + Substitute: utils.NewRSRParsersMustCompile("Al1", true), Append: true, }, }, Weight: 20, } - if !reflect.DeepEqual(attrPrf, v1Attribute.AsAttributeProfile()) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(attrPrf), utils.ToJSON(v1Attribute.AsAttributeProfile())) + if ap, err := v1Attribute.AsAttributeProfile(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(attrPrf, ap) { + t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(attrPrf), utils.ToJSON(ap)) } } diff --git a/utils/rsrparser.go b/utils/rsrparser.go index 77a873fc2..f360a47b9 100644 --- a/utils/rsrparser.go +++ b/utils/rsrparser.go @@ -56,6 +56,15 @@ func NewRSRParsersMustCompile(parsersRules string, allFiltersMatch bool) (prsrs // RSRParsers is a set of RSRParser type RSRParsers []*RSRParser +func (prsrs RSRParsers) Compile() (err error) { + for _, prsr := range prsrs { + if err = prsr.Compile(); err != nil { + return + } + } + return +} + // ParseValue will parse the value out considering converters and filters func (prsrs RSRParsers) ParseValue(value interface{}) (out string, err error) { for _, prsr := range prsrs {