diff --git a/apis/attributes_it_test.go b/apis/attributes_it_test.go index 395dbbab4..59dc914d7 100644 --- a/apis/attributes_it_test.go +++ b/apis/attributes_it_test.go @@ -60,12 +60,22 @@ var ( testAttributeGetAttributeIDs2, testAttributeGetAttributeIDsCount2, testAttributeRemoveAttributeProfile, + testAttributeGetAttributeIDs, + testAttributeGetAttributeIDsCount, testAttributeSetAttributeProfileBrokenReference, testAttributeSGetAttributeForEventMissingEvent, testAttributeSGetAttributeForEventAnyContext, testAttributeSGetAttributeForEventSameAnyContext, testAttributeSGetAttributeForEventNotFound, testAttributeSGetAttributeForEvent, + testAttributeProcessEvent, + testAttributeProcessEventWithSearchAndReplace, + testAttributeSProcessWithMultipleRuns, + testAttributeSProcessWithMultipleRuns2, + testAttributeGetAttributeProfileAllIDs, + testAttributeGetAttributeProfileAllIDsCount, + testAttributeRemoveRemainAttributeProfiles, + testAttributeGetAttributeProfileAfterRemove, testAttributeSKillEngine, } ) @@ -74,6 +84,12 @@ func TestAttributeSIT(t *testing.T) { switch *dbType { case utils.MetaInternal: alsPrfConfigDIR = "attributes_internal" + case utils.MetaMongo: + alsPrfConfigDIR = "attributes_mongo" + case utils.MetaMySQL: + alsPrfConfigDIR = "attributes_mysql" + case utils.MetaPostgres: + t.SkipNow() default: t.Fatal("Unknown Database type") } @@ -334,7 +350,7 @@ func testAttributeRemoveAttributeProfile(t *testing.T) { args, &reply); err != nil { t.Error(err) } else if reply != utils.OK { - t.Errorf("Expected %+v \n, received %+v", 2, reply) + t.Errorf("Expected %+v \n, received %+v", utils.OK, reply) } //nothing to get from db @@ -618,6 +634,468 @@ func testAttributeSGetAttributeForEvent(t *testing.T) { } } +func testAttributeProcessEvent(t *testing.T) { + var reply *engine.APIAttributeProfile + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile, + utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ATTR_1"}}, &reply); err != nil { + t.Fatal(err) + } + args := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.MetaCDRs), + ProcessRuns: utils.IntPointer(1), + CGREvent: &utils.CGREvent{ + Event: map[string]interface{}{ + utils.ToR: utils.MetaVoice, + utils.AccountField: "1002", + }, + }, + } + expEvReply := &engine.AttrSProcessEventReply{ + MatchedProfiles: []string{"TEST_ATTRIBUTES_IT_TEST"}, + AlteredFields: []string{"*tenant", utils.AccountField}, + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.itsyscom", + Event: map[string]interface{}{ + utils.AccountField: "1002", + utils.ToR: utils.MetaVoice, + }, + APIOpts: map[string]interface{}{}, + }, + } + evRply := &engine.AttrSProcessEventReply{} + if err := attrSRPC.Call(context.Background(), utils.AttributeSv1ProcessEvent, + args, &evRply); err != nil { + t.Error(err) + } else { + sort.Strings(expEvReply.AlteredFields) + sort.Strings(evRply.AlteredFields) + if !reflect.DeepEqual(evRply, expEvReply) { + t.Errorf("Expected %+v, received %+v", utils.ToJSON(expEvReply), utils.ToJSON(evRply)) + } + } +} + +func testAttributeProcessEventWithSearchAndReplace(t *testing.T) { + attrPrf1 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_Search_and_replace", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.Category:call"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Category", + Value: "~*req.Category:s/(.*)/${1}_suffix/", + }, + }, + Blocker: true, + Weight: 10, + }, + } + var result string + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf1, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + attrArgs := &engine.AttrArgsProcessEvent{ + ProcessRuns: utils.IntPointer(1), + Context: utils.StringPointer(utils.MetaSessionS), + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "HeaderEventForAttribute", + Event: map[string]interface{}{ + "Category": "call", + }, + }, + } + eRply := &engine.AttrSProcessEventReply{ + MatchedProfiles: []string{"ATTR_Search_and_replace"}, + AlteredFields: []string{"*req.Category"}, + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "HeaderEventForAttribute", + Event: map[string]interface{}{ + "Category": "call_suffix", + }, + APIOpts: map[string]interface{}{}, + }, + } + var rplyEv engine.AttrSProcessEventReply + if err := attrSRPC.Call(context.Background(), utils.AttributeSv1ProcessEvent, + attrArgs, &rplyEv); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eRply, &rplyEv) { + t.Errorf("Expecting: %s, received: %s", + utils.ToJSON(eRply), utils.ToJSON(rplyEv)) + } +} + +func testAttributeSProcessWithMultipleRuns(t *testing.T) { + attrPrf1 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_1", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.InitialField:InitialValue"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field1", + Value: "Value1", + }, + }, + Weight: 10, + }, + } + attrPrf2 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_2", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.Field1:Value1"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field2", + Value: "Value2", + }, + }, + Weight: 20, + }, + } + attrPrf3 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_3", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.NotFound:NotFound"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field3", + Value: "Value3", + }, + }, + Weight: 30, + }, + } + // Add attribute in DM + var result string + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf1, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf2, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf3, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + attrArgs := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.MetaSessionS), + ProcessRuns: utils.IntPointer(4), + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: utils.GenUUID(), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + }, + }, + } + eRply := &engine.AttrSProcessEventReply{ + MatchedProfiles: []string{"ATTR_1", "ATTR_2", "ATTR_1", "ATTR_2"}, + AlteredFields: []string{"*req.Field1", "*req.Field2"}, + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: utils.GenUUID(), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + "Field1": "Value1", + "Field2": "Value2", + }, + }, + } + + var rplyEv engine.AttrSProcessEventReply + if err := attrSRPC.Call(context.Background(), utils.AttributeSv1ProcessEvent, + attrArgs, &rplyEv); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(eRply.MatchedProfiles, rplyEv.MatchedProfiles) { + t.Errorf("Expecting %+v, received: %+v", eRply.MatchedProfiles, rplyEv.MatchedProfiles) + } + sort.Strings(rplyEv.AlteredFields) + if !reflect.DeepEqual(eRply.AlteredFields, rplyEv.AlteredFields) { + t.Errorf("Expecting %+v, received: %+v", eRply.AlteredFields, rplyEv.AlteredFields) + } else if !reflect.DeepEqual(eRply.CGREvent.Event, rplyEv.CGREvent.Event) { + t.Errorf("Expecting %+v, received: %+v", eRply.CGREvent.Event, rplyEv.CGREvent.Event) + } +} + +func testAttributeSProcessWithMultipleRuns2(t *testing.T) { + attrPrf1 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_1", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.InitialField:InitialValue"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field1", + Value: "Value1", + }, + }, + Weight: 10, + }, + } + attrPrf2 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_2", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.Field1:Value1"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field2", + Value: "Value2", + }, + }, + Weight: 20, + }, + } + attrPrf3 := &engine.AttributeWithAPIOpts{ + APIAttributeProfile: &engine.APIAttributeProfile{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: "ATTR_3", + Contexts: []string{utils.MetaSessionS}, + FilterIDs: []string{"*string:~*req.Field2:Value2"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + Attributes: []*engine.ExternalAttribute{ + { + Path: utils.MetaReq + utils.NestingSep + "Field3", + Value: "Value3", + }, + }, + Weight: 30, + }, + } + // Add attributeProfiles + var result string + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf1, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf2, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1SetAttributeProfile, + attrPrf3, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + attrArgs := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.MetaSessionS), + ProcessRuns: utils.IntPointer(4), + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: utils.GenUUID(), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + }, + }, + } + eRply := &engine.AttrSProcessEventReply{ + MatchedProfiles: []string{"ATTR_1", "ATTR_2", "ATTR_3", "ATTR_2"}, + AlteredFields: []string{"*req.Field1", "*req.Field2", "*req.Field3"}, + CGREvent: &utils.CGREvent{ + Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, + ID: utils.GenUUID(), + Event: map[string]interface{}{ + "InitialField": "InitialValue", + "Field1": "Value1", + "Field2": "Value2", + "Field3": "Value3", + }, + }, + } + + var rplyEv engine.AttrSProcessEventReply + if err := attrSRPC.Call(context.Background(), utils.AttributeSv1ProcessEvent, + attrArgs, &rplyEv); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(eRply.MatchedProfiles, rplyEv.MatchedProfiles) { + t.Errorf("Expecting %+v, received: %+v", eRply.MatchedProfiles, rplyEv.MatchedProfiles) + } + sort.Strings(rplyEv.AlteredFields) + if !reflect.DeepEqual(eRply.AlteredFields, rplyEv.AlteredFields) { + t.Errorf("Expecting %+v, received: %+v", eRply.AlteredFields, rplyEv.AlteredFields) + } else if !reflect.DeepEqual(eRply.CGREvent.Event, rplyEv.CGREvent.Event) { + t.Errorf("Expecting %+v, received: %+v", eRply.CGREvent.Event, rplyEv.CGREvent.Event) + } +} + +func testAttributeGetAttributeProfileAllIDs(t *testing.T) { + var rply []string + expectedIds := []string{"ATTR_1", "ATTR_2", "ATTR_3", "ATTR_Search_and_replace", + "TEST_ATTRIBUTES_IT_TEST"} //"TEST_ATTRIBUTES_IT_TEST_SECOND"} + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfileIDs, + &utils.PaginatorWithTenant{ + Tenant: "cgrates.org", + }, &rply); err != nil { + t.Error(err) + } else { + sort.Strings(rply) + sort.Strings(expectedIds) + if !reflect.DeepEqual(expectedIds, rply) { + t.Errorf("Expected %+v, received %+v", expectedIds, rply) + } + } +} + +func testAttributeGetAttributeProfileAllIDsCount(t *testing.T) { + var rply int + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfileIDsCount, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: "cgrates.org", + }, + }, &rply); err != nil { + t.Error(err) + } else if rply != 5 { //6 for internal + t.Errorf("Expected %+v, received %+v", 5, rply) + } +} + +func testAttributeRemoveRemainAttributeProfiles(t *testing.T) { + var reply string + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "ATTR_1", + Tenant: utils.CGRateSorg, + }, + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1RemoveAttributeProfile, + args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v \n, received %+v", utils.OK, reply) + } + + args = &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "ATTR_2", + Tenant: utils.CGRateSorg, + }, + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1RemoveAttributeProfile, + args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v \n, received %+v", utils.OK, reply) + } + + args = &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "ATTR_3", + Tenant: utils.CGRateSorg, + }, + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1RemoveAttributeProfile, + args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v \n, received %+v", utils.OK, reply) + } + + args = &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "ATTR_Search_and_replace", + Tenant: utils.CGRateSorg, + }, + } + if err := attrSRPC.Call(context.Background(), utils.AdminSv1RemoveAttributeProfile, + args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Expected %+v \n, received %+v", utils.OK, reply) + } +} + +func testAttributeGetAttributeProfileAfterRemove(t *testing.T) { + var reply *engine.APIAttributeProfile + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: utils.CGRateSorg, + ID: "ATTR_Search_and_replace", + }, + }, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: utils.CGRateSorg, + ID: "ATTR_1", + }, + }, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: utils.CGRateSorg, + ID: "ATTR_2", + }, + }, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + + if err := attrSRPC.Call(context.Background(), utils.AdminSv1GetAttributeProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: utils.CGRateSorg, + ID: "ATTR_3", + }, + }, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + //Kill the engine when it is about to be finished func testAttributeSKillEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { diff --git a/data/conf/samples/attributes_mongo/cgrates.json b/data/conf/samples/attributes_mongo/cgrates.json new file mode 100644 index 000000000..053c1fa67 --- /dev/null +++ b/data/conf/samples/attributes_mongo/cgrates.json @@ -0,0 +1,25 @@ +{ + // CGRateS Configuration file + // will be used in apis/attributes_it_test.go + + + "data_db": { + "db_type": "mongo", + "db_name": "10", + "db_port": 27017, + }, + + "stor_db": { + "db_type": "mongo", + "db_name": "cgrates", + "db_port": 27017, + }, + + "attributes": { + "enabled": true, + }, + + "admins": { + "enabled": true, + } +}, \ No newline at end of file diff --git a/data/conf/samples/attributes_mysql/cgrates.json b/data/conf/samples/attributes_mysql/cgrates.json new file mode 100644 index 000000000..be77f806d --- /dev/null +++ b/data/conf/samples/attributes_mysql/cgrates.json @@ -0,0 +1,23 @@ +{ + // CGRateS Configuration file + // will be used in apis/attributes_it_test.go + + + "data_db": { // database used to store runtime data (eg: accounts, cdr stats) + "db_type": "redis", // data_db type: + "db_port": 6379, // data_db port to reach the database + "db_name": "10", // data_db database name to connect to + }, + + "stor_db": { + "db_password": "CGRateS.org", + }, + + "attributes": { + "enabled": true, + }, + + "admins": { + "enabled": true, + } +}, \ No newline at end of file