From b1b3f4ab18e7cd141367781e31c3daef5032732b Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 24 Apr 2014 10:14:02 +0200 Subject: [PATCH] More derived charging infrastucture and tests --- apier/derivedcharging.go | 54 +++++++++++++++++++++ apier/derivedcharging_test.go | 45 +++++++++++++++++ engine/derivedcharging.go | 52 ++++++++++++++++++++ engine/derivedcharging_test.go | 88 ++++++++++++++++++++++++++++++++++ engine/loader_csv_test.go | 2 +- engine/responder.go | 6 +-- engine/responder_test.go | 43 +++++++++++++++++ engine/storage_map.go | 2 +- 8 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 apier/derivedcharging.go create mode 100644 apier/derivedcharging_test.go create mode 100644 engine/derivedcharging.go create mode 100644 engine/derivedcharging_test.go create mode 100644 engine/responder_test.go diff --git a/apier/derivedcharging.go b/apier/derivedcharging.go new file mode 100644 index 000000000..3756823cd --- /dev/null +++ b/apier/derivedcharging.go @@ -0,0 +1,54 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH + +This program is free software: you can Storagetribute 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 WITH*out 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 apier + +import ( + "fmt" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +// Get DerivedChargers applying to our call, appends general configured to account specific ones if that is configured +func (self *ApierV1) DerivedChargers(attrs utils.AttrDerivedChargers, reply *utils.DerivedChargers) (err error) { + if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Direction", "Account", "Subject"}); len(missing) != 0 { + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if hDc, err := engine.HandleGetDerivedChargers(self.AccountDb, self.Config, attrs); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else if hDc != nil { + *reply = hDc + } + return nil +} + +type AttrSetDerivedChargers struct { + Tenant, Tor, Direction, Account, Subject string + DerivedChargers utils.DerivedChargers +} + +func (self *ApierV1) SetDerivedChargers(attrs AttrSetDerivedChargers, reply *utils.DerivedChargers) (err error) { + if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Direction", "Account", "Subject", "DerivedChargers"}); len(missing) != 0 { + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + if err := self.AccountDb.SetDerivedChargers(utils.DerivedChargersKey(attrs.Tenant, attrs.Tor, attrs.Direction, attrs.Account, attrs.Subject), + attrs.DerivedChargers); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + return nil +} diff --git a/apier/derivedcharging_test.go b/apier/derivedcharging_test.go new file mode 100644 index 000000000..22db50f44 --- /dev/null +++ b/apier/derivedcharging_test.go @@ -0,0 +1,45 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH + +This program is free software: you can Storagetribute 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 WITH*out 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 apier + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "reflect" + "testing" +) + +var apierDcT *ApierV1 + +func init() { + dataStorage, _ := engine.NewMapStorage() + cfg, _ := config.NewDefaultCGRConfig() + apierDcT = &ApierV1{AccountDb: engine.AccountingStorage(dataStorage), Config: cfg} +} + +func TestGetEmptyDC(t *testing.T) { + attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Tor: "call", Direction: "*out", Account: "dan", Subject: "dan"} + var dcs utils.DerivedChargers + if err := apierDcT.DerivedChargers(attrs, &dcs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, apierDcT.Config.DerivedChargers) { + t.Error("Returned DerivedChargers not matching the configured ones") + } +} diff --git a/engine/derivedcharging.go b/engine/derivedcharging.go new file mode 100644 index 000000000..dcd30c8e9 --- /dev/null +++ b/engine/derivedcharging.go @@ -0,0 +1,52 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH + +This program is free software: you can Storagetribute 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 WITH*out 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 ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" +) + +// Transparently handles merging between storage data and configuration, useful as local handler +func HandleGetDerivedChargers(acntStorage AccountingStorage, cfg *config.CGRConfig, attrs utils.AttrDerivedChargers) (utils.DerivedChargers, error) { + var dcs utils.DerivedChargers + var err error + strictKey := utils.DerivedChargersKey(attrs.Tenant, attrs.Tor, attrs.Direction, attrs.Account, attrs.Subject) + anySubjKey := utils.DerivedChargersKey(attrs.Tenant, attrs.Tor, attrs.Direction, attrs.Account, utils.ANY) + for _, dcKey := range []string{strictKey, anySubjKey} { + if dcsDb, err := acntStorage.GetDerivedChargers(dcKey, false); err != nil && err.Error() != utils.ERR_NOT_FOUND { + return nil, err + } else if dcsDb != nil { + dcs = dcsDb + break + } + } + if dcs == nil { + dcs = cfg.DerivedChargers + return dcs, nil + } + if cfg.CombinedDerivedChargers { + for _, cfgDc := range cfg.DerivedChargers { + if dcs, err = dcs.Append(cfgDc); err != nil { + return nil, err + } + } + } + return dcs, nil +} diff --git a/engine/derivedcharging_test.go b/engine/derivedcharging_test.go new file mode 100644 index 000000000..10f0f20cb --- /dev/null +++ b/engine/derivedcharging_test.go @@ -0,0 +1,88 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH + +This program is free software: you can Storagetribute 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 WITH*out 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 ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" + "reflect" + "testing" +) + +var cfgDcT *config.CGRConfig +var acntDb AccountingStorage + +func init() { + cfgDcT, _ = config.NewDefaultCGRConfig() + acntDb, _ = NewMapStorage() + acntDb.CacheAccounting(nil, nil, nil, nil) +} + +// Accounting db has no DerivedChargers nor configured defaults +func TestHandleGetEmptyDC(t *testing.T) { + attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Tor: "call", Direction: "*out", Account: "test2", Subject: "test2"} + if dcs, err := HandleGetDerivedChargers(acntDb, cfgDcT, attrs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, cfgDcT.DerivedChargers) { + t.Error("Returned DerivedChargers not matching the configured ones") + } +} + +// Accounting db has no DerivedChargers, configured defaults +func TestHandleGetConfiguredDC(t *testing.T) { + cfgedDC := utils.DerivedChargers{&utils.DerivedCharger{RunId: "responder1", ReqTypeField: "test", DirectionField: "test", TenantField: "test", + TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}} + cfgDcT.DerivedChargers = cfgedDC + attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Tor: "call", Direction: "*out", Account: "test3", Subject: "test3"} + if dcs, err := HandleGetDerivedChargers(acntDb, cfgDcT, attrs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, cfgedDC) { + t.Error("Returned DerivedChargers not matching the configured ones") + } +} + +// Receive composed derived chargers +func TestHandleGetStoredDC(t *testing.T) { + keyCharger1 := utils.DerivedChargersKey("cgrates.org", "call", "*out", "rif", "rif") + charger1 := utils.DerivedChargers{ + &utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", TorField: "*default", + AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, + &utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", + AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, + } + if err := acntDb.SetDerivedChargers(keyCharger1, charger1); err != nil { + t.Error("Error on setDerivedChargers", err.Error()) + } + // Expected Charger should have default configured values added + expCharger1 := append(charger1, &utils.DerivedCharger{RunId: "responder1", ReqTypeField: "test", DirectionField: "test", TenantField: "test", + TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}) + acntDb.CacheAccounting(nil, nil, nil, nil) + attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Tor: "call", Direction: "*out", Account: "rif", Subject: "rif"} + if dcs, err := HandleGetDerivedChargers(acntDb, cfgDcT, attrs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, expCharger1) { + t.Error("Returned DerivedChargers not matching the configured ones") + } + cfgDcT.CombinedDerivedChargers = false + if dcs, err := HandleGetDerivedChargers(acntDb, cfgDcT, attrs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, charger1) { + t.Error("Returned DerivedChargers not matching the configured ones") + } +} diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 412051f0b..987806287 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -839,7 +839,7 @@ func TestLoadDerivedChargers(t *testing.T) { &utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, } - keyCharger1 := utils.ConcatenatedKey("cgrates.org", "call", "*out", "dan", "dan") + keyCharger1 := utils.DerivedChargersKey("cgrates.org", "call", "*out", "dan", "dan") if !reflect.DeepEqual(csvr.derivedChargers[keyCharger1], expCharger1) { t.Error("Unexpected charger", csvr.derivedChargers[keyCharger1]) } diff --git a/engine/responder.go b/engine/responder.go index 75907f2ed..f01348330 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -116,7 +116,7 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err return } -func (rs *Responder) DerivedChargers(attrs utils.AttrDerivedChargers, dcs *utils.DerivedChargers) error { +func (rs *Responder) GetDerivedChargers(attrs utils.AttrDerivedChargers, dcs *utils.DerivedChargers) error { // ToDo: Make it work with balancer if needed if dcsH, err := HandleGetDerivedChargers(accountingStorage, config.CgrConfig(), attrs); err != nil { @@ -332,7 +332,7 @@ type Connector interface { MaxDebit(CallDescriptor, *CallCost) error RefundIncrements(CallDescriptor, *float64) error GetMaxSessionTime(CallDescriptor, *float64) error - DerivedChargers(utils.AttrDerivedChargers, utils.DerivedChargers) error + GetDerivedChargers(utils.AttrDerivedChargers, utils.DerivedChargers) error } type RPCClientConnector struct { @@ -359,6 +359,6 @@ func (rcc *RPCClientConnector) GetMaxSessionTime(cd CallDescriptor, resp *float6 return rcc.Client.Call("Responder.GetMaxSessionTime", cd, resp) } -func (rcc *RPCClientConnector) DerivedChargers(attrs utils.AttrDerivedChargers, dcs utils.DerivedChargers) error { +func (rcc *RPCClientConnector) GetDerivedChargers(attrs utils.AttrDerivedChargers, dcs utils.DerivedChargers) error { return rcc.Client.Call("ApierV1.DerivedChargers", attrs, dcs) } diff --git a/engine/responder_test.go b/engine/responder_test.go new file mode 100644 index 000000000..00b322280 --- /dev/null +++ b/engine/responder_test.go @@ -0,0 +1,43 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 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 ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" + "reflect" + "testing" +) + +// Test internal abilites of GetDerivedChargers +func TestResponderGetDerivedChargers(t *testing.T) { + cfg, _ := config.NewDefaultCGRConfig() + cfgedDC := utils.DerivedChargers{&utils.DerivedCharger{RunId: "responder1", ReqTypeField: "test", DirectionField: "test", TenantField: "test", + TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}} + cfg.DerivedChargers = cfgedDC + config.SetCgrConfig(cfg) + r := Responder{} + attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Tor: "call", Direction: "*out", Account: "dan", Subject: "dan"} + var dcs utils.DerivedChargers + if err := r.GetDerivedChargers(attrs, &dcs); err != nil { + t.Error("Unexpected error", err.Error()) + } else if !reflect.DeepEqual(dcs, cfgedDC) { + t.Errorf("Expecting: %v, received: %v ", cfgedDC, dcs) + } +} diff --git a/engine/storage_map.go b/engine/storage_map.go index 29bdeec12..b2e4121e0 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -449,7 +449,7 @@ func (ms *MapStorage) GetDerivedChargers(key string, checkDb bool) (dcs utils.De err = ms.ms.Unmarshal(values, &dcs) cache2go.Cache(key, dcs) } else { - return nil, errors.New("not found") + return nil, errors.New(utils.ERR_NOT_FOUND) } return }