diff --git a/apier/v1/derivedcharging_test.go b/apier/v1/derivedcharging_test.go index de0af8b94..518690436 100644 --- a/apier/v1/derivedcharging_test.go +++ b/apier/v1/derivedcharging_test.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) 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 diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index fd907b538..74a764673 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -20,6 +20,8 @@ package v2 import ( "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "strings" ) type AttrGetAccountIds struct { @@ -40,3 +42,51 @@ func (self *ApierV2) GetAccountIds(attrs AttrGetAccountIds, reply *[]string) err *reply = accountKeys return nil } + +type AttrGetAccounts struct { + Tenant string + Account string + Offset int // Set the item offset + Limit int // Limit number of items retrieved + +} + +func (self *ApierV2) GetAccounts(attr AttrGetAccounts, reply *[]*engine.Account) error { + searchKeyPrefix := engine.ACCOUNT_PREFIX + utils.OUT + ":" + if len(attr.Tenant) != 0 { // ToDO: Update here as soon as redis 2.8 becomes last supported platform + searchKeyPrefix += attr.Tenant + ":" + if len(attr.Account) != 0 { + searchKeyPrefix += attr.Account + } + } + accountKeys, err := self.AccountDb.GetKeysForPrefix(searchKeyPrefix) + if err != nil { + return err + } else if len(accountKeys) == 0 { + return nil + } + if len(attr.Tenant) == 0 && len(attr.Account) != 0 { // Since redis version lower than 2.8 does not support masked searches in middle, we filter records out here + filteredAccounts := make([]string, 0) + for _, acntKey := range accountKeys { + if strings.HasSuffix(acntKey, ":"+attr.Account) { + filteredAccounts = append(filteredAccounts, acntKey) + } + } + accountKeys = filteredAccounts + } + var limitedAccounts []string + if attr.Limit != 0 { + limitedAccounts = accountKeys[attr.Offset : attr.Offset+attr.Limit] + } else { + limitedAccounts = accountKeys[attr.Offset:] + } + retAccounts := make([]*engine.Account, len(limitedAccounts)) + for idx, acntKey := range limitedAccounts { + retAccounts[idx], err = self.AccountDb.GetAccount(acntKey[len(engine.ACCOUNT_PREFIX):]) + if err != nil { + return err + } + } + *reply = retAccounts + return nil +} diff --git a/apier/v2/accounts_test.go b/apier/v2/accounts_test.go new file mode 100644 index 000000000..485ef8bd5 --- /dev/null +++ b/apier/v2/accounts_test.go @@ -0,0 +1,117 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 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 v2 + +import ( + "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "testing" +) + +var ( + apierAcnts *ApierV2 + apierAcntsAcntStorage *engine.MapStorage +) + +func init() { + apierAcntsAcntStorage, _ = engine.NewMapStorage() + cfg, _ := config.NewDefaultCGRConfig() + apierAcnts = &ApierV2{v1.ApierV1{AccountDb: engine.AccountingStorage(apierAcntsAcntStorage), Config: cfg}} +} + +func TestSetAccounts(t *testing.T) { + cgrTenant := "cgrates.org" + iscTenant := "itsyscom.com" + b10 := &engine.Balance{Value: 10, Weight: 10} + cgrAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account1"), + BalanceMap: map[string]engine.BalanceChain{engine.CREDIT + engine.OUTBOUND: engine.BalanceChain{b10}}} + cgrAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account2"), + BalanceMap: map[string]engine.BalanceChain{engine.CREDIT + engine.OUTBOUND: engine.BalanceChain{b10}}} + cgrAcnt3 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account3"), + BalanceMap: map[string]engine.BalanceChain{engine.CREDIT + engine.OUTBOUND: engine.BalanceChain{b10}}} + iscAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, iscTenant, "account1"), + BalanceMap: map[string]engine.BalanceChain{engine.CREDIT + engine.OUTBOUND: engine.BalanceChain{b10}}} + iscAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, iscTenant, "account2"), + BalanceMap: map[string]engine.BalanceChain{engine.CREDIT + engine.OUTBOUND: engine.BalanceChain{b10}}} + for _, account := range []*engine.Account{cgrAcnt1, cgrAcnt2, cgrAcnt3, iscAcnt1, iscAcnt2} { + if err := apierAcntsAcntStorage.SetAccount(account); err != nil { + t.Error(err) + } + } + noReload := []string{} + apierAcntsAcntStorage.CacheAccounting(nil, noReload, noReload, noReload) +} + +func TestGetAccountIds(t *testing.T) { + var accountIds []string + var attrs AttrGetAccountIds + if err := apierAcnts.GetAccountIds(attrs, &accountIds); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accountIds) != 5 { + t.Errorf("Accounts returned: %+v", accountIds) + } +} + +func TestGetAccounts(t *testing.T) { + var accounts []*engine.Account + var attrs AttrGetAccounts + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 5 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Tenant: "itsyscom.com"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 2 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Tenant: "cgrates.org", Account: "account1"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 1 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Account: "account1"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 2 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Account: "account3"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 1 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Account: "INVALID"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 0 { + t.Errorf("Accounts returned: %+v", accounts) + } + attrs = AttrGetAccounts{Tenant: "INVALID"} + if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(accounts) != 0 { + t.Errorf("Accounts returned: %+v", accounts) + } +} diff --git a/engine/storage_map.go b/engine/storage_map.go index 94b54d950..2e9967b83 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -52,8 +52,14 @@ func (ms *MapStorage) Flush(ignore string) error { return nil } -func (ms *MapStorage) GetKeysForPrefix(string) ([]string, error) { - return nil, nil +func (ms *MapStorage) GetKeysForPrefix(prefix string) ([]string, error) { + keysForPrefix := make([]string, 0) + for key := range ms.dict { + if strings.HasPrefix(key, prefix) { + keysForPrefix = append(keysForPrefix, key) + } + } + return keysForPrefix, nil } func (ms *MapStorage) CacheRating(dKeys, rpKeys, rpfKeys, alsKeys, lcrKeys []string) error { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 8eb0f1d4d..c32a6f113 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -959,17 +959,16 @@ func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*utils.Store if qryFltr.PaginatorLimit != 0 { q = q.Limit(qryFltr.PaginatorLimit) } - /* ToDo: Fix as soon as issue on Gorm analyzed: https://github.com/jinzhu/gorm/issues/354 - if qryFltr.Count { - var cnt int64 - fmt.Printf("Rows is: %+v\n", rows) - //fmt.Printf("Counting, got count: %+v\n", q.Count()) - if err := q.Count(&cnt).Error; err != nil { - fmt.Printf("Counting, got error %s", err.Error()) - return nil, 0, err + /* + // ToDo: Fix as soon as issue on Gorm analyzed: https://github.com/jinzhu/gorm/issues/354 + if qryFltr.Count { + var cnt int64 + //if err := q.Count(&cnt).Error; err != nil { + if err := q.Debug().Count(&cnt).Error; err != nil { + return nil, 0, err + } + return nil, cnt, nil } - return nil, cnt, nil - } */ // Execute query rows, err := q.Rows()