diff --git a/apier/v1/chargers.go b/apier/v1/chargers.go
index b948f1a95..6cb74da8c 100644
--- a/apier/v1/chargers.go
+++ b/apier/v1/chargers.go
@@ -86,3 +86,15 @@ func (cSv1 *ChargerSv1) Ping(ign string, reply *string) error {
*reply = utils.Pong
return nil
}
+
+// GetChargerForEvent returns matching ChargerProfile for Event
+func (cSv1 *ChargerSv1) GetChargersForEvent(cgrEv *utils.CGREvent,
+ reply *engine.ChargerProfiles) error {
+ return cSv1.cS.V1GetChargersForEvent(cgrEv, reply)
+}
+
+// ProcessEvent
+func (cSv1 *ChargerSv1) ProcessEvent(args *utils.CGREvent,
+ reply *[]*utils.CGREvent) error {
+ return cSv1.cS.V1ProcessEvent(args, reply)
+}
diff --git a/apier/v1/chargers_it_test.go b/apier/v1/chargers_it_test.go
new file mode 100755
index 000000000..2182a1ab7
--- /dev/null
+++ b/apier/v1/chargers_it_test.go
@@ -0,0 +1,274 @@
+// +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"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+var (
+ chargerCfgPath string
+ chargerCfg *config.CGRConfig
+ chargerRPC *rpc.Client
+ chargerProfile *engine.ChargerProfile
+ chargerDelay int
+ chargerConfigDIR string //run tests for specific configuration
+)
+
+var chargerEvent = []*utils.CGREvent{
+ &utils.CGREvent{ // matching Charger1
+ Tenant: "cgrates.org",
+ ID: "event1",
+ Event: map[string]interface{}{
+ utils.Account: "1001",
+ },
+ },
+ &utils.CGREvent{ // no matching
+ Tenant: "cgrates.org",
+ ID: "event1",
+ Event: map[string]interface{}{
+ utils.Account: "1010",
+ "DistinctMatch": "cgrates",
+ },
+ },
+}
+
+var sTestsCharger = []func(t *testing.T){
+ testChargerSInitCfg,
+ testChargerSInitDataDb,
+ testChargerSResetStorDb,
+ testChargerSStartEngine,
+ testChargerSRPCConn,
+ testChargerSLoadFromFolder,
+ testChargerSGetChargersForEvent,
+ testChargerSProcessEvent,
+ testChargerSSetChargerProfile,
+ testChargerSUpdateChargerProfile,
+ testChargerSRemChargerProfile,
+ testChargerSPing,
+ testChargerSKillEngine,
+}
+
+//Test start here
+func TestChargerSITMySql(t *testing.T) {
+ chargerConfigDIR = "tutmysql"
+ for _, stest := range sTestsCharger {
+ t.Run(chargerConfigDIR, stest)
+ }
+}
+
+func TestChargerSITMongo(t *testing.T) {
+ chargerConfigDIR = "tutmongo"
+ for _, stest := range sTestsCharger {
+ t.Run(chargerConfigDIR, stest)
+ }
+}
+
+func testChargerSInitCfg(t *testing.T) {
+ var err error
+ chargerCfgPath = path.Join(*dataDir, "conf", "samples", chargerConfigDIR)
+ chargerCfg, err = config.NewCGRConfigFromFolder(chargerCfgPath)
+ if err != nil {
+ t.Error(err)
+ }
+ chargerCfg.DataFolderPath = *dataDir
+ config.SetCgrConfig(chargerCfg)
+ switch chargerConfigDIR {
+ case "tutmongo":
+ chargerDelay = 2000
+ default:
+ chargerDelay = 1000
+ }
+}
+
+func testChargerSInitDataDb(t *testing.T) {
+ if err := engine.InitDataDb(chargerCfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Wipe out the cdr database
+func testChargerSResetStorDb(t *testing.T) {
+ if err := engine.InitStorDb(chargerCfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Start CGR Engine
+func testChargerSStartEngine(t *testing.T) {
+ if _, err := engine.StopStartEngine(chargerCfgPath, chargerDelay); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Connect rpc client to rater
+func testChargerSRPCConn(t *testing.T) {
+ var err error
+ chargerRPC, err = jsonrpc.Dial("tcp", chargerCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testChargerSLoadFromFolder(t *testing.T) {
+ var reply string
+ attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
+ if err := chargerRPC.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
+ t.Error(err)
+ }
+ time.Sleep(500 * time.Millisecond)
+}
+
+func testChargerSGetChargersForEvent(t *testing.T) {
+ chargerProfiles := &engine.ChargerProfiles{
+ &engine.ChargerProfile{
+ Tenant: "cgrates.org",
+ ID: "Charger1",
+ FilterIDs: []string{"*string:Account:1001"},
+ ActivationInterval: &utils.ActivationInterval{
+ ActivationTime: time.Date(2014, 7, 29, 15, 0, 0, 0, time.UTC),
+ },
+ RunID: "*rated",
+ AttributeIDs: []string{"ATTR_1001_SIMPLEAUTH"},
+ Weight: 20,
+ },
+ }
+ var result *engine.ChargerProfiles
+ if err := chargerRPC.Call(utils.ChargerSv1GetChargersForEvent, chargerEvent[1], &result); err == nil ||
+ err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+ if err := chargerRPC.Call(utils.ChargerSv1GetChargersForEvent, chargerEvent[0], &result); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(result, chargerProfiles) {
+ t.Errorf("Expecting : %s, received: %s", chargerProfiles, result)
+ }
+}
+
+func testChargerSProcessEvent(t *testing.T) {
+ processedEv := &[]*utils.CGREvent{
+ &utils.CGREvent{ // matching Charger1
+ Tenant: "cgrates.org",
+ ID: "event1",
+ Context: utils.StringPointer(utils.MetaChargers),
+ Event: map[string]interface{}{
+ utils.Account: "1001",
+ "Password": "CGRateS.org",
+ "RunID": "*rated",
+ },
+ },
+ }
+ var result *[]*utils.CGREvent
+ if err := chargerRPC.Call(utils.ChargerSv1ProcessEvent, chargerEvent[1], &result); err == nil ||
+ err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+ if err := chargerRPC.Call(utils.ChargerSv1ProcessEvent, chargerEvent[0], &result); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(result, processedEv) {
+ t.Errorf("Expecting : %s, received: %s", utils.ToJSON(processedEv), utils.ToJSON(result))
+ }
+}
+
+func testChargerSSetChargerProfile(t *testing.T) {
+ chargerProfile = &engine.ChargerProfile{
+ Tenant: "cgrates.org",
+ ID: "ApierTest",
+ FilterIDs: []string{"*string:Account:1001", "*string:Account:1002"},
+ 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),
+ },
+ RunID: "*default",
+ AttributeIDs: []string{"Attr1", "Attr2"},
+ Weight: 20,
+ }
+ var result string
+ if err := chargerRPC.Call("ApierV1.SetChargerProfile", chargerProfile, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ var reply *engine.ChargerProfile
+ if err := chargerRPC.Call("ApierV1.GetChargerProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(chargerProfile, reply) {
+ t.Errorf("Expecting : %+v, received: %+v", chargerProfile, reply)
+ }
+}
+
+func testChargerSUpdateChargerProfile(t *testing.T) {
+ chargerProfile.RunID = "*rated"
+ var result string
+ if err := chargerRPC.Call("ApierV1.SetChargerProfile", chargerProfile, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ var reply *engine.ChargerProfile
+ if err := chargerRPC.Call("ApierV1.GetChargerProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &reply); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(chargerProfile, reply) {
+ t.Errorf("Expecting : %+v, received: %+v", chargerProfile, reply)
+ }
+}
+
+func testChargerSRemChargerProfile(t *testing.T) {
+ var resp string
+ if err := chargerRPC.Call("ApierV1.RemoveChargerProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"}, &resp); err != nil {
+ t.Error(err)
+ } else if resp != utils.OK {
+ t.Error("Unexpected reply returned", resp)
+ }
+ var reply *engine.AttributeProfile
+ if err := chargerRPC.Call("ApierV1.GetChargerProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "ApierTest"},
+ &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+}
+
+func testChargerSPing(t *testing.T) {
+ var resp string
+ if err := chargerRPC.Call(utils.ChargerSv1Ping, "", &resp); err != nil {
+ t.Error(err)
+ } else if resp != utils.Pong {
+ t.Error("Unexpected reply returned", resp)
+ }
+}
+
+func testChargerSKillEngine(t *testing.T) {
+ if err := engine.KillEngine(chargerDelay); err != nil {
+ t.Error(err)
+ }
+}
diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json
index 7cd714f24..2e04f97ff 100644
--- a/data/conf/samples/tutmongo/cgrates.json
+++ b/data/conf/samples/tutmongo/cgrates.json
@@ -71,18 +71,6 @@
"thresholds_conns": [
{"address": "*internal"}
],
- "cdrstats_conns": [
- {"address": "*internal"}
- ],
- "pubsubs_conns": [
- {"address": "*internal"}
- ],
- "users_conns": [
- {"address": "*internal"}
- ],
- "aliases_conns": [
- {"address": "*internal"}
- ],
},
@@ -93,9 +81,6 @@
"cdrs": {
"enabled": true,
- "cdrstats_conns": [
- {"address": "*internal"}
- ],
},
@@ -119,19 +104,11 @@
},
-"cdrstats": {
+"chargers": {
"enabled": true,
-},
-
-
-"pubsubs": {
- "enabled": true,
-},
-
-
-"users": {
- "enabled": true,
- "indexes": ["Uuid"],
+ "attributes_conns": [
+ {"address": "*internal"}
+ ],
},
@@ -169,11 +146,6 @@
},
-"aliases": {
- "enabled": true,
-},
-
-
"sessions": {
"enabled": true,
},
diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json
index 86c108843..0c6b064e9 100644
--- a/data/conf/samples/tutmysql/cgrates.json
+++ b/data/conf/samples/tutmysql/cgrates.json
@@ -232,6 +232,9 @@
"chargers": {
"enabled": true,
+ "attributes_conns": [
+ {"address": "*internal"}
+ ],
},
diff --git a/data/tariffplans/tutorial/Chargers.csv b/data/tariffplans/tutorial/Chargers.csv
index d9f4caed0..e68860cdf 100644
--- a/data/tariffplans/tutorial/Chargers.csv
+++ b/data/tariffplans/tutorial/Chargers.csv
@@ -1 +1,2 @@
-#Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight
\ No newline at end of file
+#Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight
+cgrates.org,Charger1,*string:Account:1001,2014-07-29T15:00:00Z,*rated,ATTR_1001_SIMPLEAUTH,20
\ No newline at end of file
diff --git a/dispatcher/chargers.go b/dispatcher/chargers.go
index f362e060a..1da34b357 100755
--- a/dispatcher/chargers.go
+++ b/dispatcher/chargers.go
@@ -25,20 +25,31 @@ import (
func (dS *DispatcherService) ChargerSv1Ping(ign string, reply *string) error {
if dS.chargerS == nil {
- return utils.NewErrNotConnected(utils.AttributeS)
+ return utils.NewErrNotConnected(utils.ChargerS)
}
return dS.chargerS.Call(utils.ChargerSv1Ping, ign, reply)
}
-func (dS *DispatcherService) ChargerSv1GetChargersForEvent(args *ArgsAttrProcessEventWithApiKey,
- reply *engine.AttributeProfile) (err error) {
- if dS.attrS == nil {
- return utils.NewErrNotConnected(utils.AttributeS)
+func (dS *DispatcherService) ChargerSv1GetChargersForEvent(args *CGREvWithApiKey,
+ reply *engine.ChargerProfiles) (err error) {
+ if dS.chargerS == nil {
+ return utils.NewErrNotConnected(utils.ChargerS)
}
- if err = dS.authorize(utils.AttributeSv1GetAttributeForEvent, args.AttrArgsProcessEvent.CGREvent.Tenant,
- args.APIKey, args.AttrArgsProcessEvent.CGREvent.Time); err != nil {
+ if err = dS.authorize(utils.AttributeSv1GetAttributeForEvent, args.CGREvent.Tenant,
+ args.APIKey, args.CGREvent.Time); err != nil {
return
}
- return dS.attrS.Call(utils.AttributeSv1GetAttributeForEvent, args.AttrArgsProcessEvent, reply)
-
+ return dS.chargerS.Call(utils.ChargerSv1GetChargersForEvent, args.CGREvent, reply)
+}
+
+func (dS *DispatcherService) ChargerSv1ProcessEvent(args *CGREvWithApiKey,
+ reply *[]*utils.CGREvent) (err error) {
+ if dS.chargerS == nil {
+ return utils.NewErrNotConnected(utils.ChargerS)
+ }
+ if err = dS.authorize(utils.AttributeSv1GetAttributeForEvent, args.CGREvent.Tenant,
+ args.APIKey, args.CGREvent.Time); err != nil {
+ return
+ }
+ return dS.chargerS.Call(utils.ChargerSv1ProcessEvent, args.CGREvent, reply)
}
diff --git a/engine/chargers.go b/engine/chargers.go
index 53ff7ac79..eb1e73b88 100644
--- a/engine/chargers.go
+++ b/engine/chargers.go
@@ -141,7 +141,6 @@ func (cS *ChargerService) V1ProcessEvent(args *utils.CGREvent,
}
*reply = rply
return
-
}
// V1GetChargersForEvent exposes the list of ordered matching ChargingProfiles for an event
diff --git a/engine/chargers_test.go b/engine/chargers_test.go
new file mode 100755
index 000000000..5134a5385
--- /dev/null
+++ b/engine/chargers_test.go
@@ -0,0 +1,47 @@
+/*
+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 engine
+
+import (
+ // "reflect"
+ // "testing"
+ // "time"
+
+ "github.com/cgrates/cgrates/config"
+ //"github.com/cgrates/cgrates/utils"
+)
+
+var (
+ chargerSrv *ChargerService
+ dmCharger *DataManager
+)
+
+func TestChargerPopulateChargerService(t *testing.T) {
+ data, _ := NewMapStorage()
+ dmCharger = NewDataManager(data)
+ defaultCfg, err := config.NewDefaultCGRConfig()
+ if err != nil {
+ t.Errorf("Error: %+v", err)
+ }
+
+ chargerSrv, err = NewChargerService(dmCharger,
+ &FilterS{dm: dmAtr, cfg: defaultCfg}, nil, defaultCfg)
+ if err != nil {
+ t.Errorf("Error: %+v", err)
+ }
+}
diff --git a/engine/storage_map_datadb.go b/engine/storage_map_datadb.go
index 14be866d2..d539851a1 100644
--- a/engine/storage_map_datadb.go
+++ b/engine/storage_map_datadb.go
@@ -222,7 +222,7 @@ func (ms *MapStorage) HasDataDrv(category, subject, tenant string) (bool, error)
return exists, nil
case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.StatQueuePrefix,
utils.StatQueueProfilePrefix, utils.ThresholdPrefix, utils.ThresholdProfilePrefix,
- utils.FilterPrefix, utils.SupplierProfilePrefix, utils.AttributeProfilePrefix:
+ utils.FilterPrefix, utils.SupplierProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix:
_, exists := ms.dict[category+utils.ConcatenatedKey(tenant, subject)]
return exists, nil
}
diff --git a/engine/storage_redis.go b/engine/storage_redis.go
index 90bd12709..19e9d111c 100644
--- a/engine/storage_redis.go
+++ b/engine/storage_redis.go
@@ -274,7 +274,7 @@ func (rs *RedisStorage) HasDataDrv(category, subject, tenant string) (bool, erro
return i == 1, err
case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.StatQueuePrefix,
utils.StatQueueProfilePrefix, utils.ThresholdPrefix, utils.ThresholdProfilePrefix,
- utils.FilterPrefix, utils.SupplierProfilePrefix, utils.AttributeProfilePrefix:
+ utils.FilterPrefix, utils.SupplierProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix:
i, err := rs.Cmd("EXISTS", category+utils.ConcatenatedKey(tenant, subject)).Int()
return i == 1, err
}
diff --git a/utils/consts.go b/utils/consts.go
index 83d22c441..e402f918a 100755
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -696,7 +696,9 @@ const (
// ChargerS APIs
const (
- ChargerSv1Ping = "ChargerSv1.Ping"
+ ChargerSv1Ping = "ChargerSv1.Ping"
+ ChargerSv1GetChargersForEvent = "ChargerSv1.GetChargersForEvent"
+ ChargerSv1ProcessEvent = "ChargerSv1.ProcessEvent"
)
// ThresholdS APIs