Replaced structmatcher with normal filters

This commit is contained in:
Trial97
2021-10-12 09:48:02 +03:00
committed by Dan Christian Bogos
parent a1e0a34600
commit c671e6f08c
10 changed files with 37 additions and 855 deletions

View File

@@ -21,6 +21,8 @@ package config
import (
"reflect"
"testing"
"github.com/cgrates/cgrates/utils"
)
func TestNewObjectDP(t *testing.T) {
@@ -65,7 +67,7 @@ func TestFieldAsInterfaceObjDPInvalidSyntax(t *testing.T) {
obj: []int{12, 13},
cache: make(map[string]interface{}),
}
expected := "strconv.Atoi: parsing \"1]\": invalid syntax"
expected := utils.ErrNotFound.Error()
if _, err := objDp.FieldAsInterface(object); err == nil || err.Error() != expected {
t.Errorf("Expected %+v, received %+v", expected, err)
}

View File

@@ -221,7 +221,7 @@ func (at *ActionTiming) Execute(fltrS *FilterS) (err error) {
// check action filter
if len(a.Filter) > 0 {
if pass, err := fltrS.Pass(utils.NewTenantID(accID).Tenant, strings.Split(a.Filter, utils.InfieldSep),
config.NewObjectDP(acc)); err != nil {
utils.MapStorage{utils.MetaReq: config.NewObjectDP(acc)}); err != nil {
return err
} else if !pass {
continue

View File

@@ -71,7 +71,7 @@ func (at *ActionTrigger) Execute(ub *Account, fltrS *FilterS) (err error) {
// check action filter
if len(a.Filter) > 0 {
if pass, err := fltrS.Pass(utils.NewTenantID(a.Id).Tenant, strings.Split(a.Filter, utils.InfieldSep),
config.NewObjectDP(ub)); err != nil {
utils.MapStorage{utils.MetaReq: config.NewObjectDP(ub)}); err != nil {
return err
} else if !pass {
continue

View File

@@ -1512,7 +1512,7 @@ func TestActionTransactionFuncType(t *testing.T) {
},
},
}
err = at.Execute(nil)
at.Execute(nil)
acc, err := dm.GetAccount("cgrates.org:trans")
if err != nil || acc == nil {
t.Error("Error getting account: ", acc, err)
@@ -1807,7 +1807,7 @@ func TestActionTransferMonetaryDefault(t *testing.T) {
for _, b := range afterUb.BalanceMap[utils.MetaMonetary] {
t.Logf("B: %+v", b)
}
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
t.Error("transfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
}
}
@@ -1868,7 +1868,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) {
for _, b := range afterUb.BalanceMap[utils.MetaMonetary] {
t.Logf("B: %+v", b)
}
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
t.Error("transfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
}
}
@@ -1908,7 +1908,7 @@ func TestActionConditionalTopup(t *testing.T) {
a := &Action{
ActionType: utils.MetaTopUp,
Filter: `{"Type":"*monetary","Value":1,"Weight":10}`,
Filter: `*lt:~*req.BalanceMap.*monetary.GetTotalValue:30`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MetaMonetary),
Value: &utils.ValueFormula{Static: 11},
@@ -1920,7 +1920,9 @@ func TestActionConditionalTopup(t *testing.T) {
accountIDs: utils.StringMap{"cgrates.org:cond": true},
actions: Actions{a},
}
at.Execute(NewFilterS(config.CgrConfig(), nil, nil))
if err = at.Execute(NewFilterS(config.CgrConfig(), nil, nil)); err != nil {
t.Fatal(err)
}
afterUb, err := dm.GetAccount("cgrates.org:cond")
if err != nil {
@@ -1932,7 +1934,7 @@ func TestActionConditionalTopup(t *testing.T) {
for _, b := range afterUb.BalanceMap[utils.MetaMonetary] {
t.Logf("B: %+v", b)
}
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
t.Error("transfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
}
}
@@ -1972,7 +1974,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) {
a := &Action{
ActionType: utils.MetaTopUp,
Filter: `{"Type":"*monetary","Value":2,"Weight":10}`,
Filter: `*lt:~*req.BalanceMap.*monetary.GetTotalValue:3`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MetaMonetary),
Value: &utils.ValueFormula{Static: 11},
@@ -1995,7 +1997,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) {
for _, b := range afterUb.BalanceMap[utils.MetaMonetary] {
t.Logf("B: %+v", b)
}
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
t.Error("transfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
}
}
@@ -2036,7 +2038,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) {
a := &Action{
ActionType: utils.MetaTopUp,
Filter: `{"Type":"*voice","Value":{"*gte":100}}`,
Filter: `*gte:~*req.BalanceMap.*voice.GetTotalValue:100`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MetaMonetary),
Value: &utils.ValueFormula{Static: 11},
@@ -2059,7 +2061,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) {
for _, b := range afterUb.BalanceMap[utils.MetaMonetary] {
t.Logf("B: %+v", b)
}
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
t.Error("transfer balance value: ", afterUb.BalanceMap[utils.MetaMonetary].GetTotalValue())
}
}
@@ -2068,7 +2070,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
&Account{
ID: "cgrates.org:af",
BalanceMap: map[string]Balances{
"*data": {
utils.MetaData: {
&Balance{
Uuid: "fc927edb-1bd6-425e-a2a3-9fd8bafaa524",
ID: "for_v3hsillmilld500m_data_500_m",
@@ -2080,14 +2082,14 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
},
},
},
"*monetary": {
utils.MetaMonetary: {
&Balance{
Uuid: "9fa1847a-f36a-41a7-8ec0-dfaab370141e",
ID: utils.MetaDefault,
Value: -1.95001,
},
},
"*sms": {
utils.MetaSMS: {
&Balance{
Uuid: "d348d15d-2988-4ee4-b847-6a552f94e2ec",
ID: "for_v3hsillmilld500m_mms_ill",
@@ -2115,7 +2117,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
},
},
},
"*voice": {
utils.MetaVoice: {
&Balance{
Uuid: "079ab190-77f4-44f3-9c6f-3a0dd1a59dfd",
ID: "for_v3hsillmilld500m_voice_3_h",
@@ -2137,7 +2139,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
a1 := &Action{
ActionType: utils.MetaSetBalance,
Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}",
Filter: "*string:~*req.BalanceMap.*monetary[0].ID:*default;*lt:~*req.BalanceMap.*monetary[0].Value:0",
Balance: &BalanceFilter{
Type: utils.StringPointer("*sms"),
ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"),
@@ -2147,7 +2149,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
}
a2 := &Action{
ActionType: utils.MetaSetBalance,
Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}",
Filter: "*string:~*req.BalanceMap.*monetary[0].ID:*default;*lt:~*req.BalanceMap.*monetary[0].Value:0",
Balance: &BalanceFilter{
Type: utils.StringPointer("*sms"),
ID: utils.StringPointer("for_v3hsillmilld500m_mms_ill"),
@@ -2159,7 +2161,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
}
a3 := &Action{
ActionType: utils.MetaSetBalance,
Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}",
Filter: "*string:~*req.BalanceMap.*monetary[0].ID:*default;*lt:~*req.BalanceMap.*monetary[0].Value:0",
Balance: &BalanceFilter{
Type: utils.StringPointer("*sms"),
ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"),
@@ -2171,7 +2173,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
}
a4 := &Action{
ActionType: utils.MetaSetBalance,
Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}",
Filter: "*string:~*req.BalanceMap.*monetary[0].ID:*default;*lt:~*req.BalanceMap.*monetary[0].Value:0",
Balance: &BalanceFilter{
Type: utils.StringPointer("*data"),
Uuid: utils.StringPointer("fc927edb-1bd6-425e-a2a3-9fd8bafaa524"),
@@ -2183,7 +2185,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) {
}
a5 := &Action{
ActionType: utils.MetaSetBalance,
Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}",
Filter: "*string:~*req.BalanceMap.*monetary[0].ID:*default;*lt:~*req.BalanceMap.*monetary[0].Value:0",
Balance: &BalanceFilter{
Type: utils.StringPointer("*voice"),
ID: utils.StringPointer("for_v3hsillmilld500m_voice_3_h"),

View File

@@ -27,7 +27,6 @@ import (
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/structmatcher"
"github.com/cgrates/cgrates/utils"
)
@@ -440,7 +439,7 @@ func (tpr *TpReader) LoadActions() (err error) {
for idx, tpact := range tpacts {
// check filter field
if len(tpact.Filter) > 0 {
if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil {
if err = verifyInlineFilterS(strings.Split(tpact.Filter, utils.InfieldSep)); err != nil {
return fmt.Errorf("error parsing action %s filter field: %v", tag, err)
}
}
@@ -911,7 +910,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions)
for idx, tpact := range tpacts {
// check filter field
if len(tpact.Filter) > 0 {
if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil {
if err = verifyInlineFilterS(strings.Split(tpact.Filter, utils.InfieldSep)); err != nil {
return fmt.Errorf("error parsing action %s filter field: %v", tag, err)
}
}

View File

@@ -173,6 +173,7 @@ cgrates (0.11.0~dev) UNRELEASED; urgency=medium
* [EEs] Added *log exporter
* [AttributeS] Added profile_runs to control how many times a profile is proccessed for an event
* [DNSAgent] Updated Msg handling from templates
* [ActionsS] Replaced structmatcher with normal filters
-- DanB <danb@cgrates.org> Wed, 19 Feb 2020 13:25:52 +0200

View File

@@ -1,339 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package structmatcher
/*
The condition syntax is a json encoded string similar to mongodb query language.
Examples:
- {"Weight":{"*gt":50}} checks for a balance with weight greater than 50
- {"*or":[{"Value":{"*eq":0}},{"Value":{"*gte":100}}] checks for a balance with value equal to 0 or equal or highr than 100
Available operators:
- *eq: equal
- *gt: greater than
- *gte: greater or equal than
- *lt: less then
- *lte: less or equal than
- *exp: expired
- *or: logical or
- *and: logical and
- *not: logical not
- *has: receives a list of elements and checks that the elements are present in the specified field (StringMap type)
- *rsr: will apply a rsr check to the field (see utils/rsrfield.go)
Equal (*eq) and local and (*and) operators are implicit for shortcuts. In this way:
{"*and":[{"Value":{"*eq":3}},{"Weight":{"*eq":10}}]} is equivalent to: {"Value":3, "Weight":10}.
*/
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"time"
"github.com/cgrates/cgrates/utils"
)
const (
CondOR = "*or"
CondAND = "*and"
CondHAS = "*has"
)
func NewErrInvalidArgument(arg interface{}) error {
return fmt.Errorf("INVALID_ARGUMENT: %v", arg)
}
type StringMap map[string]bool
var (
ErrParserError = errors.New("PARSER_ERROR")
operatorMap = map[string]func(field, value interface{}) (bool, error){
utils.MetaEqual: func(field, value interface{}) (bool, error) {
return value == field, nil
},
utils.MetaGreaterThan: func(field, value interface{}) (bool, error) {
var of, vf float64
var ok bool
if of, ok = field.(float64); !ok {
return false, NewErrInvalidArgument(field)
}
if vf, ok = value.(float64); !ok {
return false, NewErrInvalidArgument(value)
}
return of > vf, nil
},
utils.MetaGreaterOrEqual: func(field, value interface{}) (bool, error) {
var of, vf float64
var ok bool
if of, ok = field.(float64); !ok {
return false, NewErrInvalidArgument(field)
}
if vf, ok = value.(float64); !ok {
return false, NewErrInvalidArgument(value)
}
return of >= vf, nil
},
utils.MetaLessThan: func(field, value interface{}) (bool, error) {
var of, vf float64
var ok bool
if of, ok = field.(float64); !ok {
return false, NewErrInvalidArgument(field)
}
if vf, ok = value.(float64); !ok {
return false, NewErrInvalidArgument(value)
}
return of < vf, nil
},
utils.MetaLessOrEqual: func(field, value interface{}) (bool, error) {
var of, vf float64
var ok bool
if of, ok = field.(float64); !ok {
return false, NewErrInvalidArgument(field)
}
if vf, ok = value.(float64); !ok {
return false, NewErrInvalidArgument(value)
}
return of <= vf, nil
},
utils.MetaExp: func(field, value interface{}) (bool, error) {
var expDate time.Time
var ok bool
if expDate, ok = field.(time.Time); !ok {
return false, NewErrInvalidArgument(field)
}
var expired bool
if expired, ok = value.(bool); !ok {
return false, NewErrInvalidArgument(value)
}
if expired { // check for expiration
return !expDate.IsZero() && expDate.Before(time.Now()), nil
} else { // check not expired
return expDate.IsZero() || expDate.After(time.Now()), nil
}
},
CondHAS: func(field, value interface{}) (bool, error) {
var strMap StringMap
var ok bool
if strMap, ok = field.(StringMap); !ok {
return false, NewErrInvalidArgument(field)
}
var strSlice []interface{}
if strSlice, ok = value.([]interface{}); !ok {
return false, NewErrInvalidArgument(value)
}
for _, str := range strSlice {
if !strMap[str.(string)] {
return false, nil
}
}
return true, nil
},
utils.MetaRSR: func(field, value interface{}) (bool, error) {
fltr, err := utils.NewRSRFilter(value.(string))
if err != nil {
return false, err
}
return fltr.Pass(fmt.Sprintf("%v", field)), nil
},
}
)
type compositeElement interface {
element
addChild(element) error
}
type element interface {
checkStruct(interface{}) (bool, error)
}
type operatorSlice struct {
operator string
slice []element
}
func (os *operatorSlice) addChild(ce element) error {
os.slice = append(os.slice, ce)
return nil
}
func (os *operatorSlice) checkStruct(o interface{}) (bool, error) {
switch os.operator {
case CondOR:
for _, cond := range os.slice {
check, err := cond.checkStruct(o)
if err != nil {
return false, err
}
if check {
return true, nil
}
}
case CondAND, utils.MetaNot:
accumulator := true
for _, cond := range os.slice {
check, err := cond.checkStruct(o)
if err != nil {
return false, err
}
accumulator = accumulator && check
}
if os.operator == CondAND {
return accumulator, nil
} else {
return !accumulator, nil
}
}
return false, nil
}
type keyStruct struct {
key string
elem element
}
func (ks *keyStruct) addChild(ce element) error {
ks.elem = ce
return nil
}
func (ks *keyStruct) checkStruct(o interface{}) (bool, error) {
obj := reflect.ValueOf(o)
if obj.Kind() == reflect.Ptr {
obj = obj.Elem()
}
value := obj.FieldByName(ks.key)
if !value.IsValid() {
return false, NewErrInvalidArgument(ks.key)
}
return ks.elem.checkStruct(value.Interface())
}
type operatorValue struct {
operator string
value interface{}
}
func (ov *operatorValue) checkStruct(o interface{}) (bool, error) {
if f, ok := operatorMap[ov.operator]; ok {
return f(o, ov.value)
}
return false, nil
}
type keyValue struct {
key string
value interface{}
}
func (kv *keyValue) checkStruct(o interface{}) (bool, error) {
obj := reflect.ValueOf(o)
if obj.Kind() == reflect.Ptr {
obj = obj.Elem()
}
value := obj.FieldByName(kv.key)
if !value.IsValid() {
return false, NewErrInvalidArgument(kv.key)
}
return value.Interface() == kv.value, nil
}
type trueElement struct{}
func (te *trueElement) checkStruct(o interface{}) (bool, error) {
return true, nil
}
func isOperator(s string) bool {
return strings.HasPrefix(s, "*")
}
type StructMatcher struct {
rootElement element
}
func NewStructMatcher(q string) (sm *StructMatcher, err error) {
sm = &StructMatcher{}
err = sm.Parse(q)
return
}
func (sm *StructMatcher) load(a map[string]interface{}, parentElement compositeElement) (element, error) {
for key, value := range a {
var currentElement element
switch t := value.(type) {
case []interface{}:
if key == CondHAS {
currentElement = &operatorValue{operator: key, value: t}
} else {
currentElement = &operatorSlice{operator: key}
for _, e := range t {
sm.load(e.(map[string]interface{}), currentElement.(compositeElement))
}
}
case map[string]interface{}:
currentElement = &keyStruct{key: key}
//log.Print("map: ", t)
sm.load(t, currentElement.(compositeElement))
case interface{}:
if isOperator(key) {
currentElement = &operatorValue{operator: key, value: t}
} else {
currentElement = &keyValue{key: key, value: t}
}
//log.Print("generic interface: ", t)
default:
return nil, ErrParserError
}
if parentElement != nil { // normal recurrent action
parentElement.addChild(currentElement)
} else {
if len(a) > 1 { // we have more keys in the map
parentElement = &operatorSlice{operator: CondAND}
parentElement.addChild(currentElement)
} else { // it was only one key value
return currentElement, nil
}
}
}
return parentElement, nil
}
func (sm *StructMatcher) Parse(s string) (err error) {
a := make(map[string]interface{})
if len(s) != 0 {
if err := json.Unmarshal([]byte([]byte(s)), &a); err != nil {
return err
}
sm.rootElement, err = sm.load(a, nil)
} else {
sm.rootElement = &trueElement{}
}
return
}
func (sm *StructMatcher) Match(o interface{}) (bool, error) {
if sm.rootElement == nil {
return false, ErrParserError
}
return sm.rootElement.checkStruct(o)
}

View File

@@ -1,483 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package structmatcher
import (
"encoding/json"
"strings"
"testing"
"time"
"github.com/cgrates/cgrates/utils"
)
func toJSON(v interface{}) string {
b, _ := json.MarshalIndent(v, "", " ")
return string(b)
}
func TestStructMatcher(t *testing.T) {
cl := &StructMatcher{}
err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
err = cl.Parse(`{"*has":["NAT","RET","EUR"]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
err = cl.Parse(`{"Field":7, "Other":true}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
err = cl.Parse(``)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
}
func TestStructMatcherKeyValue(t *testing.T) {
o := struct {
Test string
Field float64
Other bool
ExpDate time.Time
}{
Test: "test",
Field: 6.0,
Other: true,
ExpDate: time.Date(2016, 1, 19, 20, 47, 0, 0, time.UTC),
}
cl := &StructMatcher{}
err := cl.Parse(`{"Test":"test"}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":6}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Other":true}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":6, "Other":true}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":7, "Other":true}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":6, "Other":false}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Other":true, "Field":{"*gt":5}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*not":[{"Other":true, "Field":{"*gt":5}}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Other":true, "Field":{"*gt":7}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(``)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"ExpDate":{"*exp":true}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"ExpDate":{"*exp":false}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*and":[{"Field":{"*gte":50}},{"Test":{"*eq":"test"}}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"WrongFieldName":{"*eq":1}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err == nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestParseAndMatchError(t *testing.T) {
sMatch := &StructMatcher{}
expected := "invalid character 'C' looking for beginning of value"
err := sMatch.Parse("Can't unmarshal this")
if err == nil || err.Error() != expected {
t.Errorf("Expected %+v, received %+v", expected, err)
}
matchStr := &struct{}{}
_, err = sMatch.Match(matchStr)
if err == nil || err.Error() != ErrParserError.Error() {
t.Errorf("Expected %+v, received %+v", ErrParserError, err)
}
}
func TestStructMatcherKeyValuePointer(t *testing.T) {
o := &struct {
Test string
Field float64
Other bool
}{
Test: "test",
Field: 6.0,
Other: true,
}
cl := &StructMatcher{}
err := cl.Parse(`{"Test":"test"}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":6}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Other":true}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherOperatorValue(t *testing.T) {
root := &operatorValue{operator: utils.MetaGreaterThan, value: 3.4}
if check, err := root.checkStruct(3.5); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root))
}
root = &operatorValue{operator: utils.MetaEqual, value: 3.4}
if check, err := root.checkStruct(3.5); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root))
}
root = &operatorValue{operator: utils.MetaEqual, value: 3.4}
if check, err := root.checkStruct(3.4); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root))
}
root = &operatorValue{operator: utils.MetaEqual, value: "zinc"}
if check, err := root.checkStruct("zinc"); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(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, toJSON(root))
}
}
func TestStructMatcherKeyStruct(t *testing.T) {
o := struct {
Test string
Field float64
Other bool
}{
Test: "test",
Field: 6.0,
Other: true,
}
cl := &StructMatcher{}
err := cl.Parse(`{"Field":{"*gt": 5}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Test":{"*gt": 5}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*gte": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*lt": 7}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*lte": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*eq": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Test":{"*eq": "test"}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherKeyStructPointer(t *testing.T) {
o := &struct {
Test string
Field float64
Other bool
}{
Test: "test",
Field: 6.0,
Other: true,
}
cl := &StructMatcher{}
err := cl.Parse(`{"Field":{"*gt": 5}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Test":{"*gt": 5}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*gte": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*lt": 7}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*lte": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Field":{"*eq": 6}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"Test":{"*eq": "test"}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherOperatorSlice(t *testing.T) {
o := &struct {
Test string
Field float64
Other bool
}{
Test: "test",
Field: 6.0,
Other: true,
}
cl := &StructMatcher{}
err := cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*not":[{"*or":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*not":[{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherMixed(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 := &StructMatcher{}
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)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherBalanceType(t *testing.T) {
type Balance struct {
Value float64
}
o := &struct {
BalanceType string
Balance
}{
BalanceType: "*monetary",
Balance: Balance{Value: 10},
}
cl := &StructMatcher{}
err := cl.Parse(`{"BalanceType":"*monetary","Value":10}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement))
}
}
func TestStructMatcherRSR(t *testing.T) {
o := &struct {
BalanceType string
}{
BalanceType: "*monetary",
}
cl := &StructMatcher{}
err := cl.Parse(`{"BalanceType":{"*rsr":"^*mon"}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); !check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement))
}
err = cl.Parse(`{"BalanceType":{"*rsr":"^*min"}}`)
if err != nil {
t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err)
}
if check, err := cl.Match(o); check || err != nil {
t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement))
}
}

View File

@@ -804,13 +804,12 @@ func ReflectFieldMethodInterface(obj interface{}, fldName string) (retIf interfa
case reflect.Slice, reflect.Array:
//convert fldName to int
idx, err := strconv.Atoi(fldName)
if err != nil {
return nil, err
if err == nil {
if idx >= v.Len() {
return nil, fmt.Errorf("index out of range")
}
field = v.Index(idx)
}
if idx >= v.Len() {
return nil, fmt.Errorf("index out of range")
}
field = v.Index(idx)
default:
return nil, fmt.Errorf("unsupported field kind: %v", v.Kind())
}

View File

@@ -1539,8 +1539,9 @@ func TestReflectFieldMethodInterfaceArrayError(t *testing.T) {
obj := []int{2, 3, 5, 7, 11, 13}
fldName := "test"
_, err := ReflectFieldMethodInterface(obj, fldName)
if err == nil || err.Error() != "strconv.Atoi: parsing \"test\": invalid syntax" {
t.Errorf("Expected <strconv.Atoi: parsing \"test\": invalid syntax> ,received: <%+v>", err)
expected := ErrNotFound.Error()
if err == nil || err.Error() != expected {
t.Errorf("Expected <%s> ,received: <%+v>", expected, err)
}
}