diff --git a/config/objdp_test.go b/config/objdp_test.go index ee02cc0f7..c6ab221d8 100644 --- a/config/objdp_test.go +++ b/config/objdp_test.go @@ -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) } diff --git a/engine/action_plan.go b/engine/action_plan.go index fb0304f5a..f24f10c68 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -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 diff --git a/engine/action_trigger.go b/engine/action_trigger.go index c8351fb59..726f9109b 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -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 diff --git a/engine/actions_test.go b/engine/actions_test.go index 96fef54db..6aeb499d8 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -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"), diff --git a/engine/tpreader.go b/engine/tpreader.go index f2c90ce6c..1a030c396 100644 --- a/engine/tpreader.go +++ b/engine/tpreader.go @@ -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) } } diff --git a/packages/debian/changelog b/packages/debian/changelog index 63e318a5a..955a9401b 100644 --- a/packages/debian/changelog +++ b/packages/debian/changelog @@ -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 Wed, 19 Feb 2020 13:25:52 +0200 diff --git a/structmatcher/structmatcher.go b/structmatcher/structmatcher.go deleted file mode 100644 index 8e7655607..000000000 --- a/structmatcher/structmatcher.go +++ /dev/null @@ -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 -*/ -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) -} diff --git a/structmatcher/structmatcher_test.go b/structmatcher/structmatcher_test.go deleted file mode 100644 index 27a8a509d..000000000 --- a/structmatcher/structmatcher_test.go +++ /dev/null @@ -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 -*/ -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)) - } -} diff --git a/utils/reflect.go b/utils/reflect.go index fd1d4d598..9100d3127 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -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()) } diff --git a/utils/reflect_test.go b/utils/reflect_test.go index ab78f4368..8bd04b8ef 100644 --- a/utils/reflect_test.go +++ b/utils/reflect_test.go @@ -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 ,received: <%+v>", err) + expected := ErrNotFound.Error() + if err == nil || err.Error() != expected { + t.Errorf("Expected <%s> ,received: <%+v>", expected, err) } }