diff --git a/apier/v1/smgenericv1_it_test.go b/apier/v1/smgenericv1_it_test.go index 4c53fda0b..1d645d657 100644 --- a/apier/v1/smgenericv1_it_test.go +++ b/apier/v1/smgenericv1_it_test.go @@ -103,7 +103,7 @@ func TestSMGV1CacheStats(t *testing.T) { expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 10, Actions: 9, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3, StatQueues: 1, - StatQueueProfiles: 1, Thresholds: 7, ThresholdProfiles: 7, Filters: 16, LCRProfiles: 1} + StatQueueProfiles: 1, Thresholds: 7, ThresholdProfiles: 7, Filters: 16, SupplierProfiles: 2} var args utils.AttrCacheStats if err := smgV1Rpc.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) diff --git a/apier/v1/suppliers_it_test.go b/apier/v1/suppliers_it_test.go new file mode 100644 index 000000000..bafa692bb --- /dev/null +++ b/apier/v1/suppliers_it_test.go @@ -0,0 +1,136 @@ +// +build integration + +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package v1 + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + splSv1CfgPath string + splSv1Cfg *config.CGRConfig + splSv1Rpc *rpc.Client + splSv1ConfDIR string //run tests for specific configuration + splsDelay int +) + +var sTestsSupplierSV1 = []func(t *testing.T){ + testV1SplSLoadConfig, + testV1SplSInitDataDb, + testV1SplSResetStorDb, + testV1SplSStartEngine, + testV1SplSRpcConn, + testV1SplSFromFolder, + testV1SplSGetWeightSuppliers, + testV1SplSStopEngine, +} + +// Test start here +func TestSuplSV1ITMySQL(t *testing.T) { + splSv1ConfDIR = "tutmysql" + for _, stest := range sTestsSupplierSV1 { + t.Run(splSv1ConfDIR, stest) + } +} + +func TestSuplSV1ITMongo(t *testing.T) { + splSv1ConfDIR = "tutmongo" + time.Sleep(time.Duration(2 * time.Second)) // give time for engine to start + for _, stest := range sTestsSupplierSV1 { + t.Run(splSv1ConfDIR, stest) + } +} + +func testV1SplSLoadConfig(t *testing.T) { + var err error + splSv1CfgPath = path.Join(*dataDir, "conf", "samples", splSv1ConfDIR) + if splSv1Cfg, err = config.NewCGRConfigFromFolder(splSv1CfgPath); err != nil { + t.Error(err) + } + switch splSv1ConfDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + splsDelay = 4000 + default: + splsDelay = 1000 + } +} + +func testV1SplSInitDataDb(t *testing.T) { + if err := engine.InitDataDb(splSv1Cfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func testV1SplSResetStorDb(t *testing.T) { + if err := engine.InitStorDb(splSv1Cfg); err != nil { + t.Fatal(err) + } +} + +func testV1SplSStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(splSv1CfgPath, splsDelay); err != nil { + t.Fatal(err) + } +} + +func testV1SplSRpcConn(t *testing.T) { + var err error + splSv1Rpc, err = jsonrpc.Dial("tcp", splSv1Cfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal("Could not connect to rater: ", err.Error()) + } +} + +func testV1SplSFromFolder(t *testing.T) { + var reply string + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} + if err := splSv1Rpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error(err) + } + time.Sleep(500 * time.Millisecond) +} + +func testV1SplSGetWeightSuppliers(t *testing.T) { + ev := &engine.SupplierEvent{ + Tenant: "cgrates.org", + ID: "testV1SplSGetWeightSuppliers", + Event: map[string]interface{}{}, + } + var suplsReply engine.SortedSuppliers + if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers, + ev, &suplsReply); err != nil { + t.Error(err) + } +} + +func testV1SplSStopEngine(t *testing.T) { + if err := engine.KillEngine(100); err != nil { + t.Error(err) + } +} diff --git a/config/config.go b/config/config.go index 59622dfa2..3cc05aee3 100755 --- a/config/config.go +++ b/config/config.go @@ -276,6 +276,7 @@ type CGRConfig struct { resourceSCfg *ResourceSConfig // Configuration for resource limiter statsCfg *StatSCfg // Configuration for StatS thresholdSCfg *ThresholdSCfg // configuration for ThresholdS + supplierSCfg *SupplierSCfg // configuration for SupplierS MailerServer string // The server to use when sending emails out MailerAuthUser string // Authenticate to email server using this user MailerAuthPass string // Authenticate to email server with this password @@ -680,6 +681,11 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { return err } + jsnSupplierSCfg, err := jsnCfg.SupplierSJsonCfg() + if err != nil { + return err + } + jsnMailerCfg, err := jsnCfg.MailerJsonCfg() if err != nil { return err @@ -1167,6 +1173,15 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { } } + if jsnSupplierSCfg != nil { + if self.supplierSCfg == nil { + self.supplierSCfg = new(SupplierSCfg) + } + if self.supplierSCfg.loadFromJsonCfg(jsnSupplierSCfg); err != nil { + return err + } + } + if jsnUserServCfg != nil { if jsnUserServCfg.Enabled != nil { self.UserServerEnabled = *jsnUserServCfg.Enabled @@ -1234,6 +1249,10 @@ func (cfg *CGRConfig) ThresholdSCfg() *ThresholdSCfg { return cfg.thresholdSCfg } +func (cfg *CGRConfig) SuplierSCfg() *SupplierSCfg { + return cfg.supplierSCfg +} + // ToDo: fix locking here func (self *CGRConfig) SMAsteriskCfg() *SMAsteriskCfg { cfgChan := <-self.ConfigReloads[utils.SMAsterisk] // Lock config for read or reloads diff --git a/config/config_defaults.go b/config/config_defaults.go index cf2c9d4ed..449df1f51 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -446,6 +446,12 @@ const CGRATES_CFG_JSON = ` }, +"suppliers": { + "enabled": false, // starts SupplierS service: . + "indexed_fields": [], // query indexes based on these fields for faster processing +}, + + "mailer": { "server": "localhost", // the server to use when sending emails out "auth_user": "cgrates", // authenticate to email server using this user diff --git a/config/config_json.go b/config/config_json.go index c402f1da9..abd36fda3 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -60,6 +60,7 @@ const ( RESOURCES_JSON = "resources" STATS_JSON = "stats" THRESHOLDS_JSON = "thresholds" + SupplierSJson = "suppliers" FILTERS_JSON = "filters" MAILER_JSN = "mailer" SURETAX_JSON = "suretax" @@ -400,6 +401,18 @@ func (self CgrJsonCfg) ThresholdSJsonCfg() (*ThresholdSJsonCfg, error) { return cfg, nil } +func (self CgrJsonCfg) SupplierSJsonCfg() (*SupplierSJsonCfg, error) { + rawCfg, hasKey := self[SupplierSJson] + if !hasKey { + return nil, nil + } + cfg := new(SupplierSJsonCfg) + if err := json.Unmarshal(*rawCfg, cfg); err != nil { + return nil, err + } + return cfg, nil +} + func (self CgrJsonCfg) MailerJsonCfg() (*MailerJsonCfg, error) { rawCfg, hasKey := self[MAILER_JSN] if !hasKey { diff --git a/config/config_json_test.go b/config/config_json_test.go index fd4e42791..57ad67297 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -746,6 +746,18 @@ func TestDfThresholdSJsonCfg(t *testing.T) { } } +func TestDfSupplierSJsonCfg(t *testing.T) { + eCfg := &SupplierSJsonCfg{ + Enabled: utils.BoolPointer(false), + Indexed_fields: utils.StringSlicePointer([]string{}), + } + if cfg, err := dfCgrJsonCfg.SupplierSJsonCfg(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eCfg, cfg) { + t.Errorf("expecting: %+v, received: %+v", eCfg, cfg) + } +} + func TestDfMailerJsonCfg(t *testing.T) { eCfg := &MailerJsonCfg{ Server: utils.StringPointer("localhost"), diff --git a/config/config_test.go b/config/config_test.go index 2daf068bd..766481d96 100755 --- a/config/config_test.go +++ b/config/config_test.go @@ -667,7 +667,17 @@ func TestCgrCfgJSONDefaultThresholdSCfg(t *testing.T) { IndexedFields: []string{}, } if !reflect.DeepEqual(eThresholdSCfg, cgrCfg.thresholdSCfg) { - t.Errorf("received: %+v, expecting: %+v", eThresholdSCfg, cgrCfg.statsCfg) + t.Errorf("received: %+v, expecting: %+v", eThresholdSCfg, cgrCfg.thresholdSCfg) + } +} + +func TestCgrCfgJSONDefaultSupplierSCfg(t *testing.T) { + eSupplSCfg := &SupplierSCfg{ + Enabled: false, + IndexedFields: []string{}, + } + if !reflect.DeepEqual(eSupplSCfg, cgrCfg.supplierSCfg) { + t.Errorf("received: %+v, expecting: %+v", eSupplSCfg, cgrCfg.supplierSCfg) } } diff --git a/config/libconfig_json.go b/config/libconfig_json.go index ec9046d5a..e27279520 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -410,6 +410,12 @@ type ThresholdSJsonCfg struct { Indexed_fields *[]string } +// Supplier service config section +type SupplierSJsonCfg struct { + Enabled *bool + Indexed_fields *[]string +} + // Mailer config section type MailerJsonCfg struct { Server *string diff --git a/config/supplierscfg.go b/config/supplierscfg.go new file mode 100644 index 000000000..3086a959a --- /dev/null +++ b/config/supplierscfg.go @@ -0,0 +1,40 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package config + +type SupplierSCfg struct { + Enabled bool + IndexedFields []string +} + +func (spl *SupplierSCfg) loadFromJsonCfg(jsnCfg *SupplierSJsonCfg) (err error) { + if jsnCfg == nil { + return nil + } + if jsnCfg.Enabled != nil { + spl.Enabled = *jsnCfg.Enabled + } + if jsnCfg.Indexed_fields != nil { + spl.IndexedFields = make([]string, len(*jsnCfg.Indexed_fields)) + for i, fID := range *jsnCfg.Indexed_fields { + spl.IndexedFields[i] = fID + } + } + return nil +} diff --git a/utils/consts.go b/utils/consts.go index 327cf3fbb..2ec7c2d33 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -604,6 +604,11 @@ const ( MetaSetVersions = "*set_versions" ) +// MetaSupplierAPIs +const ( + SupplierSv1GetSuppliers = "SupplierSv1.GetSuppliers" +) + func buildCacheInstRevPrefixes() { CachePrefixToInstance = make(map[string]string) for k, v := range CacheInstanceToPrefix {