mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
implemented *has operator and added Type field
This commit is contained in:
@@ -697,17 +697,24 @@ func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balance
|
||||
}
|
||||
}
|
||||
|
||||
func (acc *Account) matchConditions(condition, balanceType string) (bool, error) {
|
||||
|
||||
func (acc *Account) matchConditions(condition string) (bool, error) {
|
||||
cl := &utils.CondLoader{}
|
||||
cl.Parse(condition)
|
||||
for _, b := range acc.BalanceMap[balanceType] {
|
||||
check, err := cl.Check(b)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if check {
|
||||
return true, nil
|
||||
for balanceType, balanceChain := range acc.BalanceMap {
|
||||
for _, b := range balanceChain {
|
||||
check, err := cl.Check(&struct {
|
||||
Type string
|
||||
*Balance
|
||||
}{
|
||||
Type: balanceType,
|
||||
Balance: b,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if check {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
|
||||
@@ -628,7 +628,7 @@ func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, a
|
||||
}
|
||||
|
||||
func conditionalDebitAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters); matched {
|
||||
return debitAction(acc, sq, a, acs)
|
||||
} else {
|
||||
return err
|
||||
@@ -636,7 +636,7 @@ func conditionalDebitAction(acc *Account, sq *StatsQueueTriggered, a *Action, ac
|
||||
}
|
||||
|
||||
func conditionalDebitResetAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters); matched {
|
||||
return debitResetAction(acc, sq, a, acs)
|
||||
} else {
|
||||
return err
|
||||
@@ -644,7 +644,7 @@ func conditionalDebitResetAction(acc *Account, sq *StatsQueueTriggered, a *Actio
|
||||
}
|
||||
|
||||
func conditionalTopupAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters); matched {
|
||||
return topupAction(acc, sq, a, acs)
|
||||
} else {
|
||||
return err
|
||||
@@ -652,7 +652,7 @@ func conditionalTopupAction(acc *Account, sq *StatsQueueTriggered, a *Action, ac
|
||||
}
|
||||
|
||||
func conditionalTopupResetAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters, a.BalanceType); matched {
|
||||
if matched, err := acc.matchConditions(a.ExtraParameters); matched {
|
||||
return topupResetAction(acc, sq, a, acs)
|
||||
} else {
|
||||
return err
|
||||
|
||||
@@ -2,7 +2,7 @@ package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
@@ -16,11 +16,12 @@ const (
|
||||
CondEXP = "*exp"
|
||||
CondOR = "*or"
|
||||
CondAND = "*and"
|
||||
CondHAS = "*has"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotNumerical = errors.New("NOT_NUMERICAL")
|
||||
)
|
||||
func NewErrInvalidArgument(arg interface{}) error {
|
||||
return fmt.Errorf("INVALID_ARGUMENT: %v", arg)
|
||||
}
|
||||
|
||||
type condElement interface {
|
||||
addChild(condElement) error
|
||||
@@ -87,16 +88,36 @@ type operatorValue struct {
|
||||
|
||||
func (ov *operatorValue) addChild(condElement) error { return ErrNotImplemented }
|
||||
func (ov *operatorValue) checkStruct(o interface{}) (bool, error) {
|
||||
// no conversion
|
||||
if ov.operator == CondEQ {
|
||||
return ov.value == o, nil
|
||||
}
|
||||
// StringMap conversion
|
||||
if ov.operator == CondHAS {
|
||||
var strMap StringMap
|
||||
var ok bool
|
||||
if strMap, ok = o.(StringMap); !ok {
|
||||
return false, NewErrInvalidArgument(o)
|
||||
}
|
||||
var strSlice []interface{}
|
||||
if strSlice, ok = ov.value.([]interface{}); !ok {
|
||||
return false, NewErrInvalidArgument(ov.value)
|
||||
}
|
||||
for _, str := range strSlice {
|
||||
if !strMap[str.(string)] {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
// float conversion
|
||||
var of, vf float64
|
||||
var ok bool
|
||||
if of, ok = o.(float64); !ok {
|
||||
return false, ErrNotNumerical
|
||||
return false, NewErrInvalidArgument(o)
|
||||
}
|
||||
if vf, ok = ov.value.(float64); !ok {
|
||||
return false, ErrNotNumerical
|
||||
return false, NewErrInvalidArgument(ov.value)
|
||||
}
|
||||
switch ov.operator {
|
||||
case CondGT:
|
||||
@@ -140,9 +161,13 @@ func (cp *CondLoader) load(a map[string]interface{}, parentElement condElement)
|
||||
var currentElement condElement
|
||||
switch t := value.(type) {
|
||||
case []interface{}:
|
||||
currentElement = &operatorSlice{operator: key}
|
||||
for _, e := range t {
|
||||
cp.load(e.(map[string]interface{}), currentElement)
|
||||
if key == CondHAS {
|
||||
currentElement = &operatorValue{operator: key, value: t}
|
||||
} else {
|
||||
currentElement = &operatorSlice{operator: key}
|
||||
for _, e := range t {
|
||||
cp.load(e.(map[string]interface{}), currentElement)
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
currentElement = &keyStruct{key: key}
|
||||
@@ -177,5 +202,8 @@ func (cp *CondLoader) Parse(s string) (err error) {
|
||||
}
|
||||
|
||||
func (cp *CondLoader) Check(o interface{}) (bool, error) {
|
||||
if cp.rootElement == nil {
|
||||
return false, ErrParserError
|
||||
}
|
||||
return cp.rootElement.checkStruct(o)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package utils
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCondLoader(t *testing.T) {
|
||||
cl := &CondLoader{}
|
||||
@@ -8,6 +11,15 @@ func TestCondLoader(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
|
||||
err = cl.Parse(`{"*has":["NAT","RET","EUR"]}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
err = cl.Parse(`{"Field":7, "Other":true}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondKeyValue(t *testing.T) {
|
||||
@@ -42,6 +54,20 @@ func TestCondKeyValue(t *testing.T) {
|
||||
if check, err := cl.Check(o); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
err = cl.Parse(`{"Field":6, "Other":true}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
err = cl.Parse(`{"Field":7, "Other":true}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondKeyValuePointer(t *testing.T) {
|
||||
@@ -79,22 +105,26 @@ func TestCondKeyValuePointer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCondOperatorValue(t *testing.T) {
|
||||
root := &operatorValue{operator: "*gt", value: 3.4}
|
||||
root := &operatorValue{operator: CondGT, value: 3.4}
|
||||
if check, err := root.checkStruct(3.5); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root))
|
||||
}
|
||||
root = &operatorValue{operator: "*eq", value: 3.4}
|
||||
root = &operatorValue{operator: CondEQ, value: 3.4}
|
||||
if check, err := root.checkStruct(3.5); check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root))
|
||||
}
|
||||
root = &operatorValue{operator: "*eq", value: 3.4}
|
||||
root = &operatorValue{operator: CondEQ, value: 3.4}
|
||||
if check, err := root.checkStruct(3.4); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root))
|
||||
}
|
||||
root = &operatorValue{operator: "*eq", value: "zinc"}
|
||||
root = &operatorValue{operator: CondEQ, value: "zinc"}
|
||||
if check, err := root.checkStruct("zinc"); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(root))
|
||||
}
|
||||
root = &operatorValue{operator: CondHAS, value: []interface{}{"NAT", "RET", "EUR"}}
|
||||
if check, err := root.checkStruct(StringMap{"WOR": true, "EUR": true, "NAT": true, "RET": true, "ROM": true}); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", !check, err, ToIJSON(root))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondKeyStruct(t *testing.T) {
|
||||
@@ -119,7 +149,7 @@ func TestCondKeyStruct(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); check || err != ErrNotNumerical {
|
||||
if check, err := cl.Check(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
err = cl.Parse(`{"Field":{"*gte": 6}}`)
|
||||
@@ -181,7 +211,7 @@ func TestCondKeyStructPointer(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); check || err != ErrNotNumerical {
|
||||
if check, err := cl.Check(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
err = cl.Parse(`{"Field":{"*gte": 6}}`)
|
||||
@@ -261,3 +291,47 @@ func TestCondOperatorSlice(t *testing.T) {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondMixed(t *testing.T) {
|
||||
o := &struct {
|
||||
Test string
|
||||
Field float64
|
||||
Categories StringMap
|
||||
Other bool
|
||||
}{
|
||||
Test: "test",
|
||||
Field: 6.0,
|
||||
Categories: StringMap{"call": true, "data": true, "voice": true},
|
||||
Other: true,
|
||||
}
|
||||
cl := &CondLoader{}
|
||||
err := cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true},{"Categories":{"*has":["data", "call"]}}]}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCondBalanceType(t *testing.T) {
|
||||
type Balance struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
o := &struct {
|
||||
BalanceType string
|
||||
Balance
|
||||
}{
|
||||
BalanceType: MONETARY,
|
||||
Balance: Balance{Value: 10},
|
||||
}
|
||||
cl := &CondLoader{}
|
||||
err := cl.Parse(`{"BalanceType":"*monetary","Value":10}`)
|
||||
if err != nil {
|
||||
t.Errorf("Error loading structure: %+v (%v)", ToIJSON(cl.rootElement), err)
|
||||
}
|
||||
if check, err := cl.Check(o); !check || err != nil {
|
||||
t.Errorf("Error checking struct: %v %v (%v)", !check, err, ToIJSON(cl.rootElement))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user