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 {