From b6bbc65e49dd2af72c756a689ef9886dafea8bd9 Mon Sep 17 00:00:00 2001 From: TeoV Date: Thu, 25 Jan 2018 18:15:53 +0200 Subject: [PATCH] Update AttributeProfile struct --- apier/v1/attributes.go | 11 +- apier/v1/attributes_it_test.go | 97 +++++--------- apier/v1/filter_indexes_it_test.go | 10 +- apier/v1/filterindexecache_it_test.go | 8 +- console/atributes.go | 2 +- console/atributes_set.go | 6 +- console/attributes_for_event.go | 2 +- engine/attributes.go | 25 ++-- engine/attributes_it_test.go | 96 ------------- engine/attributes_test.go | 40 ++++-- engine/datamanager.go | 10 ++ engine/libattributes.go | 59 +------- engine/model_helpers.go | 24 ++-- engine/model_helpers_test.go | 16 ++- engine/onstor_it_test.go | 185 ++++++++++++++++++++++---- 15 files changed, 287 insertions(+), 304 deletions(-) diff --git a/apier/v1/attributes.go b/apier/v1/attributes.go index 3bd120a34..ad0ba2912 100644 --- a/apier/v1/attributes.go +++ b/apier/v1/attributes.go @@ -24,7 +24,7 @@ import ( ) // GetAttributeProfile returns an Attribute Profile -func (apierV1 *ApierV1) GetAttributeProfile(arg utils.TenantID, reply *engine.ExternalAttributeProfile) error { +func (apierV1 *ApierV1) GetAttributeProfile(arg utils.TenantID, reply *engine.AttributeProfile) error { if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } @@ -34,17 +34,16 @@ func (apierV1 *ApierV1) GetAttributeProfile(arg utils.TenantID, reply *engine.Ex } return err } else { - *reply = *engine.NewExternalAttributeProfileFromAttributeProfile(alsPrf) + *reply = *alsPrf } return nil } //SetAttributeProfile add/update a new Attribute Profile -func (apierV1 *ApierV1) SetAttributeProfile(extAls *engine.ExternalAttributeProfile, reply *string) error { - if missing := utils.MissingStructFields(extAls, []string{"Tenant", "ID"}); len(missing) != 0 { +func (apierV1 *ApierV1) SetAttributeProfile(alsPrf *engine.AttributeProfile, reply *string) error { + if missing := utils.MissingStructFields(alsPrf, []string{"Tenant", "ID"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - alsPrf := extAls.AsAttributeProfile() if err := apierV1.DataManager.SetAttributeProfile(alsPrf, true); err != nil { return utils.APIErrorHandler(err) } @@ -90,7 +89,7 @@ func (alSv1 *AttributeSv1) Call(serviceMethod string, // GetAttributeForEvent returns matching AttributeProfile for Event func (alSv1 *AttributeSv1) GetAttributeForEvent(ev *utils.CGREvent, - reply *engine.ExternalAttributeProfile) error { + reply *engine.AttributeProfile) error { return alSv1.attrS.V1GetAttributeForEvent(ev, reply) } diff --git a/apier/v1/attributes_it_test.go b/apier/v1/attributes_it_test.go index d38acd6a6..60362dfa6 100644 --- a/apier/v1/attributes_it_test.go +++ b/apier/v1/attributes_it_test.go @@ -38,7 +38,7 @@ var ( alsPrfCfg *config.CGRConfig attrSRPC *rpc.Client alsPrfDataDir = "/usr/share/cgrates" - alsPrf *engine.ExternalAttributeProfile + alsPrf *engine.AttributeProfile alsPrfDelay int alsPrfConfigDIR string //run tests for specific configuration ) @@ -121,8 +121,10 @@ func testAttributeSRPCConn(t *testing.T) { } func testAttributeSGetAlsPrfBeforeSet(t *testing.T) { - var reply *engine.ExternalAttributeProfile - if err := attrSRPC.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + var reply *engine.AttributeProfile + if err := attrSRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, + &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } @@ -146,7 +148,7 @@ func testAttributeSGetAttributeForEvent(t *testing.T) { utils.Destination: "+491511231234", }, } - eAttrPrf := &engine.ExternalAttributeProfile{ + eAttrPrf := &engine.AttributeProfile{ Tenant: ev.Tenant, ID: "ATTR_1", FilterIDs: []string{"*string:Account:1007"}, @@ -169,43 +171,13 @@ func testAttributeSGetAttributeForEvent(t *testing.T) { }, Weight: 10.0, } - reverseSubstitute := []*engine.Attribute{ - &engine.Attribute{ - FieldName: utils.Subject, - Initial: utils.ANY, - Substitute: "1001", - Append: true, - }, - &engine.Attribute{ - FieldName: utils.Account, - Initial: utils.ANY, - Substitute: "1001", - Append: false, - }, - } - var attrReply *engine.ExternalAttributeProfile + var attrReply *engine.AttributeProfile if err := attrSRPC.Call(utils.AttributeSv1GetAttributeForEvent, ev, &attrReply); err != nil { t.Error(err) - } else if !reflect.DeepEqual(eAttrPrf.Tenant, attrReply.Tenant) { - t.Errorf("Expecting: %s, received: %s", eAttrPrf.Tenant, attrReply.Tenant) - } else if !reflect.DeepEqual(eAttrPrf.ID, attrReply.ID) { - t.Errorf("Expecting: %s, received: %s", eAttrPrf.ID, attrReply.ID) - } else if !reflect.DeepEqual(eAttrPrf.Contexts, attrReply.Contexts) { - t.Errorf("Expecting: %s, received: %s", eAttrPrf.Contexts, attrReply.Contexts) - } else if !reflect.DeepEqual(eAttrPrf.FilterIDs, attrReply.FilterIDs) { - t.Errorf("Expecting: %s, received: %s", eAttrPrf.FilterIDs, attrReply.FilterIDs) - } else if !reflect.DeepEqual(eAttrPrf.ActivationInterval.ActivationTime, attrReply.ActivationInterval.ActivationTime) { - t.Errorf("Expecting: %s, received: %s", - eAttrPrf.ActivationInterval.ActivationTime, attrReply.ActivationInterval.ActivationTime) - } else if !reflect.DeepEqual(eAttrPrf.ActivationInterval.ExpiryTime, attrReply.ActivationInterval.ExpiryTime) { - t.Errorf("Expecting: %s, received: %s", - eAttrPrf.ActivationInterval.ExpiryTime, attrReply.ActivationInterval.ExpiryTime) - } else if !reflect.DeepEqual(eAttrPrf.Attributes, attrReply.Attributes) && !reflect.DeepEqual(reverseSubstitute, attrReply.Attributes) { - t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eAttrPrf.Attributes), utils.ToJSON(attrReply.Attributes)) - } else if !reflect.DeepEqual(eAttrPrf.Weight, attrReply.Weight) { - t.Errorf("Expecting: %s, received: %s", eAttrPrf.Weight, attrReply.Weight) + } else if !reflect.DeepEqual(eAttrPrf, attrReply) { + t.Errorf("Expecting: %s, received: %s", utils.ToJSON(eAttrPrf), utils.ToJSON(attrReply)) } } @@ -215,8 +187,8 @@ func testAttributeSProcessEvent(t *testing.T) { ID: "testAttributeSProcessEvent", Context: utils.StringPointer(utils.MetaRating), Event: map[string]interface{}{ - "Account": "1007", - "Destination": "+491511231234", + utils.Account: "1007", + utils.Destination: "+491511231234", }, } eRply := &engine.AttrSProcessEventReply{ @@ -227,9 +199,9 @@ func testAttributeSProcessEvent(t *testing.T) { ID: "testAttributeSProcessEvent", Context: utils.StringPointer(utils.MetaRating), Event: map[string]interface{}{ - "Account": "1001", - "Subject": "1001", - "Destination": "+491511231234", + utils.Account: "1001", + utils.Subject: "1001", + utils.Destination: "+491511231234", }, }, } @@ -253,13 +225,13 @@ func testAttributeSProcessEvent(t *testing.T) { t.Error(err) } else if !reflect.DeepEqual(eRply, &rplyEv) && !reflect.DeepEqual(eRply2, &rplyEv) { // second for reversed order of attributes - t.Errorf("Expecting: %s, received: %s", - utils.ToJSON(eRply), utils.ToJSON(rplyEv)) + t.Errorf("Expecting: %s, Expecting: %s, received: %s", + utils.ToJSON(eRply), utils.ToJSON(eRply2), utils.ToJSON(rplyEv)) } } func testAttributeSSetAlsPrf(t *testing.T) { - alsPrf = &engine.ExternalAttributeProfile{ + alsPrf = &engine.AttributeProfile{ Tenant: "cgrates.org", ID: "ApierTest", Contexts: []string{"*rating"}, @@ -284,17 +256,12 @@ func testAttributeSSetAlsPrf(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } - var reply *engine.ExternalAttributeProfile - if err := attrSRPC.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err != nil { + var reply *engine.AttributeProfile + if err := attrSRPC.Call("ApierV1.GetAttributeProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err != nil { t.Error(err) - } else if !reflect.DeepEqual(alsPrf.FilterIDs, reply.FilterIDs) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.FilterIDs, reply.FilterIDs) - } else if !reflect.DeepEqual(alsPrf.ActivationInterval, reply.ActivationInterval) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.ActivationInterval, reply.ActivationInterval) - } else if !reflect.DeepEqual(len(alsPrf.Attributes), len(reply.Attributes)) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(alsPrf.Attributes), utils.ToJSON(reply.Attributes)) - } else if !reflect.DeepEqual(alsPrf.ID, reply.ID) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.ID, reply.ID) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) } } @@ -319,32 +286,28 @@ func testAttributeSUpdateAlsPrf(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile if err := attrSRPC.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err != nil { t.Error(err) - } else if !reflect.DeepEqual(alsPrf.FilterIDs, reply.FilterIDs) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.FilterIDs, reply.FilterIDs) - } else if !reflect.DeepEqual(alsPrf.ActivationInterval, reply.ActivationInterval) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.ActivationInterval, reply.ActivationInterval) - } else if !reflect.DeepEqual(len(alsPrf.Attributes), len(reply.Attributes)) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(alsPrf.Attributes), utils.ToJSON(reply.Attributes)) - } else if !reflect.DeepEqual(alsPrf.ID, reply.ID) { - t.Errorf("Expecting : %+v, received: %+v", alsPrf.ID, reply.ID) + } else if !reflect.DeepEqual(alsPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", alsPrf, reply) } } func testAttributeSRemAlsPrf(t *testing.T) { var resp string if err := attrSRPC.Call("ApierV1.RemAttributeProfile", - &ArgRemoveAttrProfile{Tenant: alsPrf.Tenant, ID: alsPrf.ID, Contexts: alsPrf.Contexts}, &resp); err != nil { + &ArgRemoveAttrProfile{Tenant: alsPrf.Tenant, ID: alsPrf.ID, + Contexts: alsPrf.Contexts}, &resp); err != nil { t.Error(err) } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) } - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile if err := attrSRPC.Call("ApierV1.GetAttributeProfile", - &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, + &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } diff --git a/apier/v1/filter_indexes_it_test.go b/apier/v1/filter_indexes_it_test.go index ac7cc7a91..8998886e4 100644 --- a/apier/v1/filter_indexes_it_test.go +++ b/apier/v1/filter_indexes_it_test.go @@ -1251,7 +1251,7 @@ func testV1FIdxRemoveSupplierProfile(t *testing.T) { //AttributeProfile func testV1FIdxSetAttributeProfileIndexes(t *testing.T) { tenant := "cgrates.org" - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile filter = &engine.Filter{ Tenant: tenant, ID: "FLTR_1", @@ -1277,7 +1277,7 @@ func testV1FIdxSetAttributeProfileIndexes(t *testing.T) { err.Error() != utils.ErrNotFound.Error() { t.Error(err) } - alsPrf = &engine.ExternalAttributeProfile{ + alsPrf = &engine.AttributeProfile{ Tenant: "cgrates.org", ID: "ApierTest", Contexts: []string{"*rating"}, @@ -1363,7 +1363,7 @@ func testV1FIdxComputeAttributeProfileIndexes(t *testing.T) { func testV1FIdxSetSecondAttributeProfileIndexes(t *testing.T) { tenant := "cgrates.org" - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile filter = &engine.Filter{ Tenant: tenant, ID: "FLTR_2", @@ -1389,7 +1389,7 @@ func testV1FIdxSetSecondAttributeProfileIndexes(t *testing.T) { err.Error() != utils.ErrNotFound.Error() { t.Error(err) } - alsPrf = &engine.ExternalAttributeProfile{ + alsPrf = &engine.AttributeProfile{ Tenant: "cgrates.org", ID: "ApierTest2", Contexts: []string{"*rating"}, @@ -1504,7 +1504,7 @@ func testV1FIdxRemoveAttributeProfile(t *testing.T) { } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) } - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile if err := tFIdxRpc.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: tenant, ID: "ApierTest2"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) diff --git a/apier/v1/filterindexecache_it_test.go b/apier/v1/filterindexecache_it_test.go index a6e18102d..9f71eb847 100644 --- a/apier/v1/filterindexecache_it_test.go +++ b/apier/v1/filterindexecache_it_test.go @@ -973,7 +973,7 @@ func testV1FIdxCaSetAttributeProfile(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } - alsPrf := &engine.ExternalAttributeProfile{ + alsPrf := &engine.AttributeProfile{ Tenant: "cgrates.org", ID: "TEST_PROFILE1", Contexts: []string{"*rating"}, @@ -1081,7 +1081,7 @@ func testV1FIdxCaUpdateAttributeProfile(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } - alsPrf := &engine.ExternalAttributeProfile{ + alsPrf := &engine.AttributeProfile{ Tenant: "cgrates.org", ID: "TEST_PROFILE1", Contexts: []string{"*rating"}, @@ -1162,7 +1162,7 @@ func testV1FIdxCaUpdateAttributeProfileFromTP(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } - var reply *engine.ExternalAttributeProfile + var reply *engine.AttributeProfile if err := tFIdxCaRpc.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: "cgrates.org", ID: "ATTR_1"}, &reply); err != nil { t.Error(err) } @@ -1233,7 +1233,7 @@ func testV1FIdxCaRemoveAttributeProfile(t *testing.T) { } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) } - var sqp *engine.ExternalAttributeProfile + var sqp *engine.AttributeProfile //Test the remove if err := tFIdxCaRpc.Call("ApierV1.GetAttributeProfile", &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &sqp); err == nil || diff --git a/console/atributes.go b/console/atributes.go index 9a1022ee2..114f64ece 100644 --- a/console/atributes.go +++ b/console/atributes.go @@ -61,6 +61,6 @@ func (self *CmdGetAtributes) PostprocessRpcParams() error { } func (self *CmdGetAtributes) RpcResult() interface{} { - atr := engine.ExternalAttributeProfile{} + atr := engine.AttributeProfile{} return &atr } diff --git a/console/atributes_set.go b/console/atributes_set.go index 1b0fae6e4..beaa1e9d7 100644 --- a/console/atributes_set.go +++ b/console/atributes_set.go @@ -24,7 +24,7 @@ func init() { c := &CmdSetAttributes{ name: "attributes_set", rpcMethod: "ApierV1.SetAttributeProfile", - rpcParams: &engine.ExternalAttributeProfile{}, + rpcParams: &engine.AttributeProfile{}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -33,7 +33,7 @@ func init() { type CmdSetAttributes struct { name string rpcMethod string - rpcParams *engine.ExternalAttributeProfile + rpcParams *engine.AttributeProfile *CommandExecuter } @@ -47,7 +47,7 @@ func (self *CmdSetAttributes) RpcMethod() string { func (self *CmdSetAttributes) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &engine.ExternalAttributeProfile{} + self.rpcParams = &engine.AttributeProfile{} } return self.rpcParams } diff --git a/console/attributes_for_event.go b/console/attributes_for_event.go index 1b436ff7c..e9bff6ca8 100644 --- a/console/attributes_for_event.go +++ b/console/attributes_for_event.go @@ -76,6 +76,6 @@ func (self *CmdGetAttributeForEvent) PostprocessRpcParams() error { } func (self *CmdGetAttributeForEvent) RpcResult() interface{} { - atr := engine.ExternalAttributeProfile{} + atr := engine.AttributeProfile{} return &atr } diff --git a/engine/attributes.go b/engine/attributes.go index 9717d39b8..140644b46 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -73,6 +73,7 @@ func (alS *AttributeService) matchingAttributeProfilesForEvent(ev *utils.CGREven lockIDs := utils.PrefixSliceItems(aPrflIDs.Slice(), utils.AttributeFilterIndexes) guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) + //utils.Logger.Debug(fmt.Sprintf("ID %+v", aPrflIDs)) for apID := range aPrflIDs { aPrfl, err := alS.dm.GetAttributeProfile(ev.Tenant, apID, false, utils.NonTransactional) if err != nil { @@ -105,6 +106,7 @@ func (alS *AttributeService) matchingAttributeProfilesForEvent(ev *utils.CGREven i++ } aPrfls.Sort() + utils.Logger.Debug(fmt.Sprintf("aPrfls %+v", utils.ToJSON(aPrfls))) return } @@ -150,7 +152,7 @@ func (alS *AttributeService) processEvent(ev *utils.CGREvent) (rply *AttrSProces return nil, err } rply = &AttrSProcessEventReply{MatchedProfile: attrPrf.ID, CGREvent: ev.Clone()} - for fldName, intialMp := range attrPrf.Attributes { + for fldName, intialMp := range attrPrf.attributes { initEvValIf, has := ev.Event[fldName] if !has { // we don't have initial in event, try append if anyInitial, has := intialMp[utils.ANY]; has && anyInitial.Append { @@ -159,14 +161,14 @@ func (alS *AttributeService) processEvent(ev *utils.CGREvent) (rply *AttrSProces } continue } - initEvVal, cast := utils.CastFieldIfToString(initEvValIf) - if !cast { - utils.Logger.Warning( - fmt.Sprintf("<%s> ev: %s, cannot cast field: %+v to string", - utils.AttributeS, ev, fldName)) - continue - } - attrVal, has := intialMp[initEvVal] + // initEvVal, cast := utils.CastFieldIfToString(initEvValIf) + // if !cast { + // utils.Logger.Warning( + // fmt.Sprintf("<%s> ev: %s, cannot cast field: %+v to string", + // utils.AttributeS, ev, fldName)) + // continue + // } + attrVal, has := intialMp[initEvValIf] if !has { attrVal, has = intialMp[utils.ANY] } @@ -187,7 +189,7 @@ func (alS *AttributeService) processEvent(ev *utils.CGREvent) (rply *AttrSProces } func (alS *AttributeService) V1GetAttributeForEvent(ev *utils.CGREvent, - extattrPrf *ExternalAttributeProfile) (err error) { + attrPrfl *AttributeProfile) (err error) { attrPrf, err := alS.attributeProfileForEvent(ev) if err != nil { if err != utils.ErrNotFound { @@ -195,8 +197,7 @@ func (alS *AttributeService) V1GetAttributeForEvent(ev *utils.CGREvent, } return err } - eattrPrfl := NewExternalAttributeProfileFromAttributeProfile(attrPrf) - *extattrPrf = *eattrPrfl + *attrPrfl = *attrPrf return } diff --git a/engine/attributes_it_test.go b/engine/attributes_it_test.go index 238c44cde..747f32dd8 100644 --- a/engine/attributes_it_test.go +++ b/engine/attributes_it_test.go @@ -21,106 +21,10 @@ package engine import ( "reflect" "testing" - "time" "github.com/cgrates/cgrates/utils" ) -func TestExternalAttributeProfileAsAttributeProfile(t *testing.T) { - extAttr := &ExternalAttributeProfile{ - Tenant: "cgrates.org", - ID: "ALS1", - Contexts: []string{"con1"}, - FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - }, - Attributes: []*Attribute{ - &Attribute{ - FieldName: "FL1", - Initial: "In1", - Substitute: "Al1", - Append: true, - }, - }, - Weight: 20, - } - attrMap := make(map[string]map[string]*Attribute) - attrMap["FL1"] = make(map[string]*Attribute) - attrMap["FL1"]["In1"] = &Attribute{ - FieldName: "FL1", - Initial: "In1", - Substitute: "Al1", - Append: true, - } - expected := &AttributeProfile{ - Tenant: "cgrates.org", - ID: "ALS1", - Contexts: []string{"con1"}, - FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - }, - Attributes: attrMap, - Weight: 20, - } - - rcv := extAttr.AsAttributeProfile() - if !reflect.DeepEqual(expected, rcv) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) - } -} - -func TestNewExternalAttributeProfileFromAttributeProfile(t *testing.T) { - attrMap := make(map[string]map[string]*Attribute) - attrMap["FL1"] = make(map[string]*Attribute) - attrMap["FL1"]["In1"] = &Attribute{ - FieldName: "FL1", - Initial: "In1", - Substitute: "Al1", - Append: true, - } - attrPrf := &AttributeProfile{ - Tenant: "cgrates.org", - ID: "ALS1", - Contexts: []string{"con1"}, - FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - }, - Attributes: attrMap, - Weight: 20, - } - - expected := &ExternalAttributeProfile{ - Tenant: "cgrates.org", - ID: "ALS1", - Contexts: []string{"con1"}, - FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), - }, - Attributes: []*Attribute{ - &Attribute{ - FieldName: "FL1", - Initial: "In1", - Substitute: "Al1", - Append: true, - }, - }, - Weight: 20, - } - - rcv := NewExternalAttributeProfileFromAttributeProfile(attrPrf) - if !reflect.DeepEqual(expected, rcv) { - t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) - } -} - func TestAttrSProcessEventReplyDigest(t *testing.T) { eRpl := &AttrSProcessEventReply{ MatchedProfile: "ATTR_1", diff --git a/engine/attributes_test.go b/engine/attributes_test.go index da28720d7..27f927aab 100644 --- a/engine/attributes_test.go +++ b/engine/attributes_test.go @@ -40,20 +40,20 @@ func TestPopulateAttrService(t *testing.T) { data, _ := NewMapStorage() dmAtr = NewDataManager(data) context := utils.MetaRating - attrMap := make(map[string]map[string]*Attribute) - attrMap["FL1"] = make(map[string]*Attribute) - attrMap["FL1"]["In1"] = &Attribute{ - FieldName: "FL1", - Initial: "In1", - Substitute: "Al1", - Append: true, - } //Need clone because time.Now add extra information and DeepEqual don't like var cloneExpTime time.Time expTime := time.Now().Add(time.Duration(20 * time.Minute)) if err := utils.Clone(expTime, &cloneExpTime); err != nil { t.Error(err) } + mapSubstitutes := make(map[string]map[interface{}]*Attribute) + mapSubstitutes["FL1"] = make(map[interface{}]*Attribute) + mapSubstitutes["FL1"]["In1"] = &Attribute{ + FieldName: "FL1", + Initial: "In1", + Substitute: "Al1", + Append: true, + } atrPs = AttributeProfiles{ &AttributeProfile{ Tenant: "cgrates.org", @@ -64,8 +64,16 @@ func TestPopulateAttrService(t *testing.T) { ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), ExpiryTime: cloneExpTime, }, - Attributes: attrMap, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FL1", + Initial: "In1", + Substitute: "Al1", + Append: true, + }, + }, Weight: 20, + attributes: mapSubstitutes, }, &AttributeProfile{ Tenant: "cgrates.org", @@ -76,8 +84,16 @@ func TestPopulateAttrService(t *testing.T) { ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), ExpiryTime: cloneExpTime, }, - Attributes: attrMap, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FL1", + Initial: "In1", + Substitute: "Al1", + Append: true, + }, + }, Weight: 20, + attributes: mapSubstitutes, }, } x, err := NewRequestFilter(MetaString, "attributeprofile1", []string{"Attribute"}) @@ -145,7 +161,7 @@ func TestAttributeMatchingAttributeProfilesForEvent(t *testing.T) { t.Errorf("Error: %+v", err) } if !reflect.DeepEqual(atrPs[0], atrpl[0]) && !reflect.DeepEqual(atrPs[0], atrpl[1]) { - t.Errorf("Expecting: %+v, received: %+v ", atrPs[0], atrpl[0]) + t.Errorf("Expecting: %+v, received: %+v ", utils.ToJSON(atrPs[0]), utils.ToJSON(atrpl[0])) } else if !reflect.DeepEqual(atrPs[1], atrpl[1]) && !reflect.DeepEqual(atrPs[1], atrpl[0]) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(atrPs), utils.ToJSON(atrpl)) } @@ -168,7 +184,7 @@ func TestAttributeProfileForEvent(t *testing.T) { t.Errorf("Error: %+v", err) } if !reflect.DeepEqual(atrPs[0], atrpl) && !reflect.DeepEqual(atrPs[1], atrpl) { - t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(atrPs[0]), utils.ToJSON(atrpl)) + t.Errorf("Expecting: %+v, received: %+v", atrPs[0], atrpl) } } diff --git a/engine/datamanager.go b/engine/datamanager.go index 19b37f426..3b7649983 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -1165,6 +1165,16 @@ func (dm *DataManager) GetAttributeProfile(tenant, id string, skipCache bool, tr } return nil, err } + for _, attr := range alsPrf.Attributes { + alsPrf.attributes = make(map[string]map[interface{}]*Attribute) + 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, + } + } cache.Set(key, alsPrf, cacheCommit(transactionID), transactionID) return } diff --git a/engine/libattributes.go b/engine/libattributes.go index 60dbae69f..407ba8123 100644 --- a/engine/libattributes.go +++ b/engine/libattributes.go @@ -26,8 +26,8 @@ import ( type Attribute struct { FieldName string - Initial string - Substitute string + Initial interface{} + Substitute interface{} Append bool } @@ -36,8 +36,9 @@ type AttributeProfile struct { ID string Contexts []string // bind this AttributeProfile to multiple contexts FilterIDs []string - ActivationInterval *utils.ActivationInterval // Activation interval - Attributes map[string]map[string]*Attribute // map[FieldName][InitialValue]*Attribute + ActivationInterval *utils.ActivationInterval // Activation interval + attributes map[string]map[interface{}]*Attribute // map[FieldName][InitialValue]*Attribute + Attributes []*Attribute Weight float64 } @@ -52,53 +53,3 @@ type AttributeProfiles []*AttributeProfile func (aps AttributeProfiles) Sort() { sort.Slice(aps, func(i, j int) bool { return aps[i].Weight > aps[j].Weight }) } - -type ExternalAttributeProfile struct { - Tenant string - ID string - Contexts []string // bind this AttributeProfile to multiple context - FilterIDs []string - ActivationInterval *utils.ActivationInterval // Activation interval - Attributes []*Attribute - Weight float64 -} - -func (eap *ExternalAttributeProfile) AsAttributeProfile() *AttributeProfile { - alsPrf := &AttributeProfile{ - Tenant: eap.Tenant, - ID: eap.ID, - Contexts: eap.Contexts, - FilterIDs: eap.FilterIDs, - ActivationInterval: eap.ActivationInterval, - Weight: eap.Weight, - } - alsMap := make(map[string]map[string]*Attribute) - for _, als := range eap.Attributes { - alsMap[als.FieldName] = make(map[string]*Attribute) - alsMap[als.FieldName][als.Initial] = als - } - alsPrf.Attributes = alsMap - return alsPrf -} - -func NewExternalAttributeProfileFromAttributeProfile(alsPrf *AttributeProfile) *ExternalAttributeProfile { - extals := &ExternalAttributeProfile{ - Tenant: alsPrf.Tenant, - ID: alsPrf.ID, - Contexts: alsPrf.Contexts, - ActivationInterval: alsPrf.ActivationInterval, - FilterIDs: alsPrf.FilterIDs, - Weight: alsPrf.Weight, - } - for key, val := range alsPrf.Attributes { - for key2, val2 := range val { - extals.Attributes = append(extals.Attributes, &Attribute{ - FieldName: key, - Initial: key2, - Substitute: val2.Substitute, - Append: val2.Append, - }) - } - } - return extals -} diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 333507b5b..034dd4288 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -2773,12 +2773,11 @@ func APItoModelTPAttribute(th *utils.TPAttributeProfile) (mdls TPAttributes) { func APItoAttributeProfile(tpTH *utils.TPAttributeProfile, timezone string) (th *AttributeProfile, err error) { th = &AttributeProfile{ - Tenant: tpTH.Tenant, - ID: tpTH.ID, - Weight: tpTH.Weight, - FilterIDs: []string{}, - Contexts: []string{}, - Attributes: make(map[string]map[string]*Attribute, len(tpTH.Attributes)), + Tenant: tpTH.Tenant, + ID: tpTH.ID, + Weight: tpTH.Weight, + FilterIDs: []string{}, + Contexts: []string{}, } for _, fli := range tpTH.FilterIDs { th.FilterIDs = append(th.FilterIDs, fli) @@ -2787,10 +2786,15 @@ func APItoAttributeProfile(tpTH *utils.TPAttributeProfile, timezone string) (th th.Contexts = append(th.Contexts, context) } for _, reqAttr := range tpTH.Attributes { - if _, has := th.Attributes[reqAttr.FieldName]; !has { - th.Attributes[reqAttr.FieldName] = make(map[string]*Attribute) - } - th.Attributes[reqAttr.FieldName][reqAttr.Initial] = &Attribute{ + th.Attributes = append(th.Attributes, &Attribute{ + Append: reqAttr.Append, + FieldName: reqAttr.FieldName, + Initial: reqAttr.Initial, + Substitute: reqAttr.Substitute, + }) + th.attributes = make(map[string]map[interface{}]*Attribute) + th.attributes[reqAttr.FieldName] = make(map[interface{}]*Attribute) + th.attributes[reqAttr.FieldName][reqAttr.Initial] = &Attribute{ FieldName: reqAttr.FieldName, Initial: reqAttr.Initial, Substitute: reqAttr.Substitute, diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 9dc71f51c..a3eb4783c 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -1175,9 +1175,9 @@ func TestAPItoAttributeProfile(t *testing.T) { }, Weight: 20, } - attrMap := make(map[string]map[string]*Attribute) - attrMap["FL1"] = make(map[string]*Attribute) - attrMap["FL1"]["In1"] = &Attribute{ + mapSubstitutes := make(map[string]map[interface{}]*Attribute) + mapSubstitutes["FL1"] = make(map[interface{}]*Attribute) + mapSubstitutes["FL1"]["In1"] = &Attribute{ FieldName: "FL1", Initial: "In1", Substitute: "Al1", @@ -1191,8 +1191,16 @@ func TestAPItoAttributeProfile(t *testing.T) { ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), }, - Attributes: attrMap, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FL1", + Initial: "In1", + Substitute: "Al1", + Append: true, + }, + }, Weight: 20, + attributes: mapSubstitutes, } if rcv, err := APItoAttributeProfile(tpAlsPrf, "UTC"); err != nil { t.Error(err) diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index 47c2d3fc0..b5e7a3c17 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -86,6 +86,8 @@ var sTestsOnStorIT = []func(t *testing.T){ testOnStorITTestThresholdFilterIndexes, testOnStorITTestAttributeProfileFilterIndexes, testOnStorITTestThresholdInlineFilterIndexing, + testOnStorITFlush, + testOnStorITTestAttributeSubstituteIface, //testOnStorITCacheActionTriggers, //testOnStorITCacheAlias, //testOnStorITCacheReverseAlias, @@ -113,7 +115,7 @@ func TestOnStorITRedis(t *testing.T) { } func TestOnStorITMongoConnect(t *testing.T) { - sleepDelay = 500 * time.Millisecond + sleepDelay = 50 * time.Millisecond cdrsMongoCfgPath := path.Join(*dataDir, "conf", "samples", "cdrsv2mongo") mgoITCfg, err := config.NewCGRConfigFromFolder(cdrsMongoCfgPath) if err != nil { @@ -761,10 +763,10 @@ func testOnStorITRatingPlan(t *testing.T) { StartTime: "00:00:00", }, } - time.Sleep(sleepDelay) if err := onStor.SetRatingPlan(rp, utils.NonTransactional); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetRatingPlan(rp.Id, false, utils.NonTransactional); err != nil { t.Error(err) @@ -839,10 +841,10 @@ func testOnStorITRatingProfile(t *testing.T) { CdrStatQueueIds: []string{}, }, } - time.Sleep(sleepDelay) if err := onStor.SetRatingProfile(rpf, utils.NonTransactional); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetRatingProfile(rpf.Id, false, utils.NonTransactional); err != nil { @@ -1232,11 +1234,11 @@ func testOnStorITActions(t *testing.T) { }, }, } - time.Sleep(sleepDelay) if err := onStor.SetActions(acts[0].Id, acts, utils.NonTransactional); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetActions(acts[0].Id, false, utils.NonTransactional); err != nil { @@ -1801,10 +1803,10 @@ func testOnStorITResourceProfile(t *testing.T) { } //update rL.Thresholds = []string{"TH1", "TH2"} - time.Sleep(sleepDelay) if err := onStor.SetResourceProfile(rL, false); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetResourceProfile(rL.Tenant, rL.ID, false, utils.NonTransactional); err != nil { @@ -1877,10 +1879,10 @@ func testOnStorITResource(t *testing.T) { } //update res.TTLIdx = []string{"RU1", "RU2"} - time.Sleep(sleepDelay) if err := onStor.SetResource(res); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetResource("cgrates.org", "RL1", false, utils.NonTransactional); err != nil { @@ -1947,10 +1949,10 @@ func testOnStorITTiming(t *testing.T) { } //update tmg.MonthDays = utils.MonthDays{1, 2, 3, 4, 5, 6, 7} - time.Sleep(sleepDelay) if err := onStor.SetTiming(tmg); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetTiming(tmg.ID, false, utils.NonTransactional); err != nil { t.Error(err) @@ -2054,10 +2056,10 @@ func testOnStorITStatQueueProfile(t *testing.T) { } //update sq.Thresholds = []string{"TH1", "TH2"} - time.Sleep(sleepDelay) if err := onStor.SetStatQueueProfile(sq, false); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetStatQueueProfile(sq.Tenant, sq.ID, false, utils.NonTransactional); err != nil { @@ -2150,10 +2152,10 @@ func testOnStorITStatQueue(t *testing.T) { }, }, } - time.Sleep(sleepDelay) if err := onStor.SetStatQueue(sq); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetStatQueue(sq.Tenant, sq.ID, false, utils.NonTransactional); err != nil { @@ -2243,10 +2245,10 @@ func testOnStorITThresholdProfile(t *testing.T) { } //update th.ActionIDs = []string{"Action1", "Action2"} - time.Sleep(sleepDelay) if err := onStor.SetThresholdProfile(th, true); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetThresholdProfile(th.Tenant, th.ID, false, utils.NonTransactional); err != nil { @@ -2313,10 +2315,10 @@ func testOnStorITThreshold(t *testing.T) { } //update th.Hits = 20 - time.Sleep(sleepDelay) if err := onStor.SetThreshold(th); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetThreshold("cgrates.org", "TH1", false, utils.NonTransactional); err != nil { @@ -2402,10 +2404,10 @@ func testOnStorITFilter(t *testing.T) { Values: []string{"10", "20"}, }, } - time.Sleep(sleepDelay) if err := onStor.SetFilter(fp); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetFilter("cgrates.org", "Filter1", false, utils.NonTransactional); err != nil { @@ -2509,10 +2511,10 @@ func testOnStorITSupplierProfile(t *testing.T) { SupplierParameters: "param2", }, } - time.Sleep(sleepDelay) if err := onStor.SetSupplierProfile(splProfile, false); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetSupplierProfile("cgrates.org", "SPRF_1", false, utils.NonTransactional); err != nil { @@ -2544,8 +2546,8 @@ func testOnStorITSupplierProfile(t *testing.T) { } func testOnStorITAttributeProfile(t *testing.T) { - mapSubstitutes := make(map[string]map[string]*Attribute) - mapSubstitutes["FN1"] = make(map[string]*Attribute) + mapSubstitutes := make(map[string]map[interface{}]*Attribute) + mapSubstitutes["FN1"] = make(map[interface{}]*Attribute) mapSubstitutes["FN1"]["Init1"] = &Attribute{ FieldName: "FN1", Initial: "Init1", @@ -2559,9 +2561,17 @@ func testOnStorITAttributeProfile(t *testing.T) { ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), }, - Contexts: []string{"con1"}, - Attributes: mapSubstitutes, + Contexts: []string{"con1"}, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: "Val1", + Append: true, + }, + }, Weight: 20, + attributes: mapSubstitutes, } if _, rcvErr := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", false, utils.NonTransactional); rcvErr != nil && rcvErr != utils.ErrNotFound { @@ -2591,11 +2601,11 @@ func testOnStorITAttributeProfile(t *testing.T) { t.Errorf("Expected : %+v, but received %+v", expectedT, itm) } //update - time.Sleep(sleepDelay) attrProfile.Contexts = []string{"con1", "con2", "con3"} if err := onStor.SetAttributeProfile(attrProfile, false); err != nil { t.Error(err) } + time.Sleep(sleepDelay) //get from cache if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", false, utils.NonTransactional); err != nil { @@ -2892,14 +2902,6 @@ func testOnStorITTestAttributeProfileFilterIndexes(t *testing.T) { if err := onStor.SetFilter(fp); err != nil { t.Error(err) } - mapSubstitutes := make(map[string]map[string]*Attribute) - mapSubstitutes["FN1"] = make(map[string]*Attribute) - mapSubstitutes["FN1"]["Init1"] = &Attribute{ - FieldName: "FN1", - Initial: "Init1", - Substitute: "Val1", - Append: true, - } attrProfile := &AttributeProfile{ Tenant: "cgrates.org", ID: "AttrPrf", @@ -2907,9 +2909,16 @@ func testOnStorITTestAttributeProfileFilterIndexes(t *testing.T) { ActivationInterval: &utils.ActivationInterval{ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), }, - Contexts: []string{"con1", "con2"}, - Attributes: mapSubstitutes, - Weight: 20, + Contexts: []string{"con1", "con2"}, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: "Val1", + Append: true, + }, + }, + Weight: 20, } //Set AttributeProfile with 2 contexts ( con1 , con2) if err := onStor.SetAttributeProfile(attrProfile, true); err != nil { @@ -3137,3 +3146,121 @@ func testOnStorITTestThresholdInlineFilterIndexing(t *testing.T) { t.Error(err) } } + +func testOnStorITTestAttributeSubstituteIface(t *testing.T) { + //set Substitue with type string + mapSubstitutes := make(map[string]map[interface{}]*Attribute) + mapSubstitutes["FN1"] = make(map[interface{}]*Attribute) + mapSubstitutes["FN1"]["Init1"] = &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: "Val1", + Append: true, + } + attrProfile := &AttributeProfile{ + Tenant: "cgrates.org", + ID: "AttrPrf1", + FilterIDs: []string{"FLTR_ACNT_dan", "FLTR_DST_DE"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Contexts: []string{"con1"}, + Attributes: []*Attribute{ + &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: "Val1", + Append: true, + }, + }, + Weight: 20, + attributes: mapSubstitutes, + } + if _, rcvErr := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + false, utils.NonTransactional); rcvErr != nil && rcvErr != utils.ErrNotFound { + t.Error(rcvErr) + } + if err := onStor.SetAttributeProfile(attrProfile, false); err != nil { + t.Error(err) + } + //check cache + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", attrProfile, rcv) + } + //check database + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + true, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", attrProfile, rcv) + } + //set Substitue with type float + mapSubstitutes["FN1"]["Init1"] = &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: 123.5, + Append: true, + } + attrProfile.Attributes = []*Attribute{ + &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: 123.5, + Append: true, + }, + } + if err := onStor.SetAttributeProfile(attrProfile, false); err != nil { + t.Error(err) + } + attrProfile.attributes = mapSubstitutes + //check cache + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", utils.ToJSON(attrProfile), utils.ToJSON(rcv)) + } + //check database + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + true, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", utils.ToJSON(attrProfile), utils.ToJSON(rcv)) + } + //set Substitue with type bool + mapSubstitutes["FN1"]["Init1"] = &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: true, + Append: true, + } + attrProfile.Attributes = []*Attribute{ + &Attribute{ + FieldName: "FN1", + Initial: "Init1", + Substitute: true, + Append: true, + }, + } + if err := onStor.SetAttributeProfile(attrProfile, false); err != nil { + t.Error(err) + } + attrProfile.attributes = mapSubstitutes + //check cache + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", utils.ToJSON(attrProfile), utils.ToJSON(rcv)) + } + //check database + if rcv, err := onStor.GetAttributeProfile("cgrates.org", "AttrPrf1", + true, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(attrProfile, rcv)) { + t.Errorf("Expecting: %v, received: %v", utils.ToJSON(attrProfile), utils.ToJSON(rcv)) + } +}