mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Replaced structmatcher with normal filters
This commit is contained in:
committed by
Dan Christian Bogos
parent
a1e0a34600
commit
c671e6f08c
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user