diff --git a/apier/v1/apier.go b/apier/v1/apier.go
index 7120dc1da..169f3a7ea 100644
--- a/apier/v1/apier.go
+++ b/apier/v1/apier.go
@@ -21,7 +21,6 @@ package apier
import (
"errors"
"fmt"
-
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/scheduler"
@@ -42,17 +41,20 @@ type ApierV1 struct {
Config *config.CGRConfig
}
-type AttrDestination struct {
- Id string
- Prefixes []string
-}
-
-func (self *ApierV1) GetDestination(attr *AttrDestination, reply *AttrDestination) error {
- if dst, err := self.DataDb.GetDestination(attr.Id, false); err != nil {
+func (self *ApierV1) GetDestination(dstId string, reply *engine.Destination) error {
+ if dst, err := self.DataDb.GetDestination(dstId, false); err != nil {
return errors.New(utils.ERR_NOT_FOUND)
} else {
- reply.Id = dst.Id
- reply.Prefixes = dst.Prefixes
+ *reply = *dst
+ }
+ return nil
+}
+
+func (self *ApierV1) GetRatingPlan(rplnId string, reply *engine.RatingPlan) error {
+ if rpln, err := self.DataDb.GetRatingPlan(rplnId, false); err != nil {
+ return errors.New(utils.ERR_NOT_FOUND)
+ } else {
+ *reply = *rpln
}
return nil
}
@@ -93,7 +95,6 @@ type AttrAddBalance struct {
}
func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error {
- // what storage instance do we use?
tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
if _, err := self.DataDb.GetUserBalance(tag); err != nil {
diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go
index b6f9e51e6..97dbcee5f 100644
--- a/apier/v1/apier_local_test.go
+++ b/apier/v1/apier_local_test.go
@@ -56,12 +56,16 @@ var testLocal = flag.Bool("local", false, "Perform the tests only on local test
var dataDir = flag.String("data_dir", "/usr/share/cgrates/data", "CGR data dir path here")
var waitRater = flag.Int("wait_rater", 300, "Number of miliseconds to wait for rater to start and cache")
+func init() {
+ cfgPath := path.Join(*dataDir, "conf", "cgrates.cfg")
+ cfg, _ = config.NewCGRConfig(&cfgPath)
+}
+
// Empty tables before using them
func TestCreateTables(t *testing.T) {
if !*testLocal {
return
}
- cfg, _ = config.NewDefaultCGRConfig()
var mysql *engine.MySQLStorage
if d, err := engine.NewMySQLStorage(cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass); err != nil {
t.Fatal("Error on opening database connection: ", err)
@@ -81,6 +85,19 @@ func TestCreateTables(t *testing.T) {
}
}
+func TestInitDataDb(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ dataDb, err := engine.ConfigureDataStorage(cfg.DataDBType, cfg.DataDBHost, cfg.DataDBPort, cfg.DataDBName, cfg.DataDBUser, cfg.DataDBPass, cfg.DBDataEncoding)
+ if err != nil {
+ t.Fatal("Cannot connect to dataDb", err)
+ }
+ if err := dataDb.Flush(); err != nil {
+ t.Fatal("Cannot reset dataDb", err)
+ }
+}
+
// Finds cgr-engine executable and starts it with default configuration
func TestStartEngine(t *testing.T) {
if !*testLocal {
@@ -299,10 +316,13 @@ func TestApierTPDestinationRate(t *testing.T) {
dr := &utils.TPDestinationRate{TPid: engine.TEST_SQL, DestinationRateId: "DR_FREESWITCH_USERS", DestinationRates: []*utils.DestinationRate{
&utils.DestinationRate{DestinationId: "FS_USERS", RateId: "RT_FS_USERS"},
}}
+ drDe := &utils.TPDestinationRate{TPid: engine.TEST_SQL, DestinationRateId: "DR_FREESWITCH_USERS", DestinationRates: []*utils.DestinationRate{
+ &utils.DestinationRate{DestinationId: "GERMANY_MOBILE", RateId: "RT_FS_USERS"},
+ }}
dr2 := new(utils.TPDestinationRate)
*dr2 = *dr
dr2.DestinationRateId = engine.TEST_SQL
- for _, d := range []*utils.TPDestinationRate{dr, dr2} {
+ for _, d := range []*utils.TPDestinationRate{dr, dr2, drDe} {
if err := rater.Call("ApierV1.SetTPDestinationRate", d, &reply); err != nil {
t.Error("Got error on ApierV1.SetTPDestinationRate: ", err.Error())
} else if reply != "OK" {
@@ -459,10 +479,9 @@ func TestApierTPActions(t *testing.T) {
}
reply := ""
act := &utils.TPActions{TPid: engine.TEST_SQL, ActionsId: "PREPAID_10", Actions: []*utils.TPAction{
- &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight:10},
+ &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight: 10},
&utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Direction: "*out", Units: 10, ExpiryTime: "*unlimited",
DestinationId: "*any", BalanceWeight: 10, Weight: 10},
-
}}
actTst := new(utils.TPActions)
*actTst = *act
@@ -717,21 +736,6 @@ func TestApierSetAccountActions(t *testing.T) {
}
}
-// Test here LoadTariffPlanFromFolder
-func TestApierLoadTariffPlanFromFolder(t *testing.T) {
- if !*testLocal {
- return
- }
- reply := ""
- // Simple test that command is executed without errors
- attrs := &AttrLoadTPFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")}
- if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
- t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error())
- } else if reply != "OK" {
- t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply)
- }
-}
-
// Test here ReloadScheduler
func TestApierReloadScheduler(t *testing.T) {
if !*testLocal {
@@ -761,7 +765,110 @@ func TestApierReloadCache(t *testing.T) {
}
}
-// Test here ReloadCache
+// Test here GetDestination
+func TestApierGetDestination(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ reply := new(engine.Destination)
+ dstId := "GERMANY_MOBILE"
+ expectedReply := &engine.Destination{Id: dstId, Prefixes: []string{"+4915", "+4916", "+4917"}}
+ if err := rater.Call("ApierV1.GetDestination", dstId, reply); err != nil {
+ t.Error("Got error on ApierV1.GetDestination: ", err.Error())
+ } else if !reflect.DeepEqual(expectedReply, reply) {
+ t.Errorf("Calling ApierV1.GetDestination expected: %v, received: %v", expectedReply, reply)
+ }
+}
+
+// Test here GetRatingPlan
+func TestApierGetRatingPlan(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ reply := new(engine.RatingPlan)
+ rplnId := "RETAIL1"
+ //riTiming := &engine.RITiming{StartTime: "00:00:00"}
+ rt := &engine.Rate{GroupIntervalStart: 0, Value: 0, RateIncrement: time.Duration(60)*time.Second, RateUnit: time.Duration(60)*time.Second}
+ riRate := &engine.RIRate{ConnectFee:0, RoundingMethod: "*up", RoundingDecimals: 0, Rates: []*engine.Rate{rt}}
+ //{"Id":"RETAIL1","Timings":{"96c78ff5":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""}},"Ratings":{"e41ffcf2":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":0}},"DestinationRates":{"FS_USERS":[{"Timing":"96c78ff5","Rating":"e41ffcf2","Weight":10}],"GERMANY_MOBILE":[{"Timing":"96c78ff5","Rating":"e41ffcf2","Weight":10}]}
+ if err := rater.Call("ApierV1.GetRatingPlan", rplnId, reply); err != nil {
+ t.Error("Got error on ApierV1.GetRatingPlan: ", err.Error())
+ }
+ // Check parts of info received since a full one is not possible due to unique map keys inside reply
+ if reply.Id != rplnId {
+ t.Error("Unexpected id received", reply.Id)
+ }
+ if len(reply.Timings)!= 1 || len(reply.Ratings) != 1 {
+ t.Error("Unexpected number of items received")
+ }
+ //for _, tm := range reply.Timings { // We only get one loop
+ // if !reflect.DeepEqual(tm, riTiming) {
+ // t.Errorf("Unexpected timings value: %v, expecting: %v", tm, riTiming)
+ // }
+ //}
+ for _, rating := range reply.Ratings {
+ if !reflect.DeepEqual( rating, riRate ) {
+ t.Errorf("Unexpected riRate received: %v", rating)
+ }
+ }
+}
+
+// Test here AddBalance
+func TestApierAddBalance(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ reply := ""
+ attrs := &AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out", Value: 1.5}
+ if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil {
+ t.Error("Got error on ApierV1.AddBalance: ", err.Error())
+ } else if reply != "OK" {
+ t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
+ }
+ attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out", Value: 1.5}
+ if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil {
+ t.Error("Got error on ApierV1.AddBalance: ", err.Error())
+ } else if reply != "OK" {
+ t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
+ }
+}
+
+// Test here GetBalance
+func TestApierGetBalance(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ var reply float64
+ attrs := &AttrGetBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out"}
+ if err := rater.Call("ApierV1.GetBalance", attrs, &reply); err != nil {
+ t.Error("Got error on ApierV1.GetBalance: ", err.Error())
+ } else if reply != 11.5 { // We expect 11.5 since we have added in the previous test 1.5
+ t.Errorf("Calling ApierV1.GetBalance expected: 11.5, received: %f", reply)
+ }
+ attrs = &AttrGetBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out"}
+ if err := rater.Call("ApierV1.GetBalance", attrs, &reply); err != nil {
+ t.Error("Got error on ApierV1.GetBalance: ", err.Error())
+ } else if reply != 1.5 {
+ t.Errorf("Calling ApierV1.GetBalance expected: 1.5, received: %f", reply)
+ }
+}
+
+// Test here LoadTariffPlanFromFolder
+func TestApierLoadTariffPlanFromFolder(t *testing.T) {
+ if !*testLocal {
+ return
+ }
+ reply := ""
+ // Simple test that command is executed without errors
+ attrs := &AttrLoadTPFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")}
+ if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
+ t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error())
+ } else if reply != "OK" {
+ t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply)
+ }
+}
+
+// Test here ResponderGetCost
func TestResponderGetCost(t *testing.T) {
if !*testLocal {
return
diff --git a/console/destination.go b/console/destination.go
index 30fb9b0e1..9642cbbb0 100644
--- a/console/destination.go
+++ b/console/destination.go
@@ -20,7 +20,7 @@ package console
import (
"fmt"
- "github.com/cgrates/cgrates/apier/v1"
+ "github.com/cgrates/cgrates/engine"
)
func init() {
@@ -30,8 +30,8 @@ func init() {
// Commander implementation
type CmdGetDestination struct {
rpcMethod string
- rpcParams *apier.AttrDestination
- rpcResult *apier.AttrDestination
+ rpcParams string
+ rpcResult *engine.Destination
}
// name should be exec's name
@@ -42,7 +42,6 @@ func (self *CmdGetDestination) Usage(name string) string {
// set param defaults
func (self *CmdGetDestination) defaults() error {
self.rpcMethod = "Apier.GetDestination"
- self.rpcParams = &apier.AttrDestination{}
return nil
}
@@ -53,7 +52,7 @@ func (self *CmdGetDestination) FromArgs(args []string) error {
}
// Args look OK, set defaults before going further
self.defaults()
- self.rpcParams.Id = args[2]
+ self.rpcParams = args[2]
return nil
}
@@ -66,6 +65,6 @@ func (self *CmdGetDestination) RpcParams() interface{} {
}
func (self *CmdGetDestination) RpcResult() interface{} {
- self.rpcResult = &apier.AttrDestination{}
+ self.rpcResult = new(engine.Destination)
return self.rpcResult
}
diff --git a/engine/calldesc.go b/engine/calldesc.go
index 3dfc9c931..6096fbf33 100644
--- a/engine/calldesc.go
+++ b/engine/calldesc.go
@@ -19,9 +19,9 @@ along with this program. If not, see
package engine
import (
+ "encoding/json"
"errors"
"fmt"
- "encoding/json"
"github.com/cgrates/cgrates/cache2go"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/utils"
@@ -496,7 +496,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
defer storageGetter.SetUserBalance(userBalance)
ub, _ := json.Marshal(userBalance)
Logger.Debug(fmt.Sprintf("UserBalance: %s", ub))
- cCost,_ := json.Marshal(cc)
+ cCost, _ := json.Marshal(cc)
Logger.Debug(fmt.Sprintf("CallCost: %s", cCost))
if cc.Cost != 0 || cc.ConnectFee != 0 {
userBalance.debitCreditBalance(cc, true)
diff --git a/engine/loader_helpers_test.go b/engine/loader_helpers_test.go
index fb793466b..6faa92e71 100644
--- a/engine/loader_helpers_test.go
+++ b/engine/loader_helpers_test.go
@@ -19,12 +19,12 @@ along with this program. If not, see
package engine
import (
+ "bufio"
"github.com/cgrates/cgrates/utils"
- "strings"
- "testing"
"io"
"reflect"
- "bufio"
+ "strings"
+ "testing"
)
var timingsSample = `#Tag,Years,Months,MonthDays,WeekDays,Time
@@ -81,7 +81,6 @@ cgrates.org,1001,*out,PREPAID_10,STANDARD_TRIGGERS
DUMMY,INVALID;DATA
`
-
func TestTimingsValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(timingsSample))
lnValidator := FileValidators[utils.TIMINGS_CSV]
@@ -98,7 +97,7 @@ func TestTimingsValidator(t *testing.T) {
if valid {
t.Error("Validation passed for invalid line", ln)
}
- case 2,4:
+ case 2, 4:
if !valid {
t.Error("Validation did not pass for valid line", ln)
}
@@ -106,7 +105,6 @@ func TestTimingsValidator(t *testing.T) {
}
}
-
func TestDestinationsValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(destsSample))
lnValidator := FileValidators[utils.DESTINATIONS_CSV]
@@ -123,7 +121,7 @@ func TestDestinationsValidator(t *testing.T) {
if valid {
t.Error("Validation passed for invalid line", string(ln))
}
- case 2,4:
+ case 2, 4:
if !valid {
t.Error("Validation did not pass for valid line", string(ln))
}
@@ -131,7 +129,6 @@ func TestDestinationsValidator(t *testing.T) {
}
}
-
func TestRatesValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(ratesSample))
lnValidator := FileValidators[utils.RATES_CSV]
@@ -156,7 +153,6 @@ func TestRatesValidator(t *testing.T) {
}
}
-
func TestDestRatesValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(destRatesSample))
lnValidator := FileValidators[utils.DESTINATION_RATES_CSV]
@@ -181,7 +177,6 @@ func TestDestRatesValidator(t *testing.T) {
}
}
-
func TestRatingPlansValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(ratingPlansSample))
lnValidator := FileValidators[utils.RATING_PLANS_CSV]
@@ -230,7 +225,6 @@ func TestRatingProfilesValidator(t *testing.T) {
}
}
-
func TestActionsValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(actionsSample))
lnValidator := FileValidators[utils.ACTIONS_CSV]
@@ -247,7 +241,7 @@ func TestActionsValidator(t *testing.T) {
if valid {
t.Error("Validation passed for invalid line", string(ln))
}
- case 2,3,4:
+ case 2, 3, 4:
if !valid {
t.Error("Validation did not pass for valid line", string(ln))
}
@@ -255,7 +249,6 @@ func TestActionsValidator(t *testing.T) {
}
}
-
func TestActionTimingsValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(actionTimingsSample))
lnValidator := FileValidators[utils.ACTION_TIMINGS_CSV]
@@ -280,7 +273,6 @@ func TestActionTimingsValidator(t *testing.T) {
}
}
-
func TestActionTriggersValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(actionTriggersSample))
lnValidator := FileValidators[utils.ACTION_TRIGGERS_CSV]
@@ -297,7 +289,7 @@ func TestActionTriggersValidator(t *testing.T) {
if valid {
t.Error("Validation passed for invalid line", string(ln))
}
- case 2,3,4:
+ case 2, 3, 4:
if !valid {
t.Error("Validation did not pass for valid line", string(ln))
}
@@ -305,7 +297,6 @@ func TestActionTriggersValidator(t *testing.T) {
}
}
-
func TestAccountActionsValidator(t *testing.T) {
reader := bufio.NewReader(strings.NewReader(accountActionsSample))
lnValidator := FileValidators[utils.ACCOUNT_ACTIONS_CSV]
@@ -329,7 +320,6 @@ func TestAccountActionsValidator(t *testing.T) {
}
}
}
-
func TestTPCSVFileParser(t *testing.T) {
bfRdr := bufio.NewReader(strings.NewReader(ratesSample))
@@ -350,15 +340,13 @@ func TestTPCSVFileParser(t *testing.T) {
if err != nil {
t.Error(err)
}
- if !reflect.DeepEqual( record, []string{"RT_1CENT","0","1","1s","1s","0s","*up","2"}) {
+ if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "1", "1s", "1s", "0s", "*up", "2"}) {
t.Error("Unexpected record extracted", record)
}
case 3:
- if err==nil {
+ if err == nil {
t.Error("Expecting invalid line at row 3")
}
}
}
}
-
-
diff --git a/engine/realcalls_test.go b/engine/realcalls_test.go
index e63988731..ff92da840 100644
--- a/engine/realcalls_test.go
+++ b/engine/realcalls_test.go
@@ -19,15 +19,14 @@ along with this program. If not, see
package engine
import (
- "testing"
"encoding/json"
+ "testing"
)
var balance1 string = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","BalanceMap":{"*monetary*out":[{"Uuid":"7fe5d6e740b6edd180b96274b8bd4123","Value":10,"ExpirationDate":"0001-01-01T00:00:00Z","Weight":10,"GroupIds":null,"DestinationId":"*any","RateSubject":""}]},"UnitCounters":null,"ActionTriggers":[{"Id":"120ea04d40af91c580adb0da11554c88","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":2,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"fa217a904059cfd3806239f5ad229f4a","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_balance","ThresholdValue":20,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"f05174b740ab987c802a0a29aa5a2764","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_counter","ThresholdValue":15,"DestinationId":"FS_USERS","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"4d6ebf454048371280100094246163a7","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":0.1,"DestinationId":"","Weight":10,"ActionsId":"WARN_HTTP","Executed":false}],"Groups":null,"UserIds":null}`
var callCost1 string = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":0.6,"ConnectFee":0,"Timespans":[{"TimeStart":"2013-12-03T14:36:48+01:00","TimeEnd":"2013-12-03T14:37:48+01:00","Cost":0.6,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0.6,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
-
func TestDebitBalanceForCall1(t *testing.T) {
b1 := new(UserBalance)
if err := json.Unmarshal([]byte(balance1), b1); err != nil {
@@ -50,7 +49,6 @@ var balanceInsufficient = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","Bal
var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
-
func SomeTestDebitInsufficientBalance(t *testing.T) {
b1 := new(UserBalance)
if err := json.Unmarshal([]byte(balanceInsufficient), b1); err != nil {
diff --git a/engine/storage_map.go b/engine/storage_map.go
index 9363afff7..ab045282b 100644
--- a/engine/storage_map.go
+++ b/engine/storage_map.go
@@ -22,11 +22,11 @@ import (
"errors"
"fmt"
- "strings"
- "time"
"github.com/cgrates/cgrates/cache2go"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/utils"
+ "strings"
+ "time"
)
type MapStorage struct {