mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-21 23:28:44 +05:00
Add ReflectFieldMethodInterface method in utils + tests for it
This commit is contained in:
committed by
Dan Christian Bogos
parent
373801e5b1
commit
2e742aa947
@@ -49,3 +49,57 @@ func GetDynamicString(dnVal string, dP DataProvider) (string, error) {
|
||||
}
|
||||
return dnVal, nil
|
||||
}
|
||||
|
||||
//NewObjectDP constructs a DataProvider
|
||||
func NewObjectDP(obj interface{}) (dP DataProvider) {
|
||||
dP = &ObjectDP{obj: obj, cache: NewNavigableMap(nil)}
|
||||
return
|
||||
}
|
||||
|
||||
type ObjectDP struct {
|
||||
obj interface{}
|
||||
cache *NavigableMap
|
||||
}
|
||||
|
||||
// String is part of engine.DataProvider interface
|
||||
// when called, it will display the already parsed values out of cache
|
||||
func (objDP *ObjectDP) String() string {
|
||||
return utils.ToJSON(objDP.obj)
|
||||
}
|
||||
|
||||
// FieldAsInterface is part of engine.DataProvider interface
|
||||
func (objDP *ObjectDP) FieldAsInterface(fldPath []string) (data interface{}, err error) {
|
||||
// []string{ BalanceMap *monetary[0] Value }
|
||||
if data, err = objDP.cache.FieldAsInterface(fldPath); err == nil ||
|
||||
err != utils.ErrNotFound { // item found in cache
|
||||
return
|
||||
}
|
||||
err = nil // cancel previous err
|
||||
// for _, fld := range fldPath {
|
||||
|
||||
// //process each field
|
||||
// }
|
||||
objDP.cache.Set(fldPath, data, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// FieldAsString is part of engine.DataProvider interface
|
||||
func (objDP *ObjectDP) FieldAsString(fldPath []string) (data string, err error) {
|
||||
var valIface interface{}
|
||||
valIface, err = objDP.FieldAsInterface(fldPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return utils.IfaceAsString(valIface), nil
|
||||
}
|
||||
|
||||
// AsNavigableMap is part of engine.DataProvider interface
|
||||
func (objDP *ObjectDP) AsNavigableMap([]*FCTemplate) (
|
||||
nm *NavigableMap, err error) {
|
||||
return nil, utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// RemoteHost is part of engine.DataProvider interface
|
||||
func (objDP *ObjectDP) RemoteHost() net.Addr {
|
||||
return utils.LocalAddr()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -1172,57 +1171,3 @@ func (as *AccountSummary) Clone() (cln *AccountSummary) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// newAccountCache constructs a DataProvider
|
||||
func NewAccountDP(account *Account) (dP config.DataProvider) {
|
||||
dP = &AccountDP{account: account, cache: config.NewNavigableMap(nil)}
|
||||
return
|
||||
}
|
||||
|
||||
// AccountCache implements engine.DataProvider so we can pass it to filters
|
||||
type AccountDP struct {
|
||||
account *Account
|
||||
cache *config.NavigableMap
|
||||
}
|
||||
|
||||
// String is part of engine.DataProvider interface
|
||||
// when called, it will display the already parsed values out of cache
|
||||
func (accDP *AccountDP) String() string {
|
||||
return utils.ToJSON(accDP.account)
|
||||
}
|
||||
|
||||
// FieldAsInterface is part of engine.DataProvider interface
|
||||
func (accDP *AccountDP) FieldAsInterface(fldPath []string) (data interface{}, err error) {
|
||||
if len(fldPath) != 1 {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
if data, err = accDP.cache.FieldAsInterface(fldPath); err == nil ||
|
||||
err != utils.ErrNotFound { // item found in cache
|
||||
return
|
||||
}
|
||||
err = nil // cancel previous err
|
||||
//check the field in the account
|
||||
accDP.cache.Set(fldPath, data, false, false)
|
||||
return
|
||||
}
|
||||
|
||||
// FieldAsString is part of engine.DataProvider interface
|
||||
func (accDP *AccountDP) FieldAsString(fldPath []string) (data string, err error) {
|
||||
var valIface interface{}
|
||||
valIface, err = accDP.FieldAsInterface(fldPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return utils.IfaceAsString(valIface), nil
|
||||
}
|
||||
|
||||
// AsNavigableMap is part of engine.DataProvider interface
|
||||
func (accDP *AccountDP) AsNavigableMap([]*config.FCTemplate) (
|
||||
nm *config.NavigableMap, err error) {
|
||||
return nil, utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
// RemoteHost is part of engine.DataProvider interface
|
||||
func (accDP *AccountDP) RemoteHost() net.Addr {
|
||||
return utils.LocalAddr()
|
||||
}
|
||||
|
||||
@@ -664,7 +664,7 @@ func (fS *FilterS) getFieldNameDataProvider(initialDP config.DataProvider, field
|
||||
&utils.AttrGetAccount{Tenant: tenant, Account: "completeHereWithID"}, &account); err != nil {
|
||||
return
|
||||
}
|
||||
dp = NewAccountDP(account)
|
||||
dp = config.NewObjectDP(account)
|
||||
case strings.HasPrefix(fieldName, utils.MetaResources):
|
||||
case strings.HasPrefix(fieldName, utils.MetaStats):
|
||||
default:
|
||||
@@ -684,7 +684,7 @@ func (fS *FilterS) getFieldValueDataProviders(initialDP config.DataProvider, val
|
||||
&utils.AttrGetAccount{Tenant: tenant, Account: "completeHereWithID"}, &account); err != nil {
|
||||
return
|
||||
}
|
||||
dp[i] = NewAccountDP(account)
|
||||
dp[i] = config.NewObjectDP(account)
|
||||
case strings.HasPrefix(val, utils.MetaResources):
|
||||
case strings.HasPrefix(val, utils.MetaStats):
|
||||
default:
|
||||
|
||||
@@ -542,3 +542,54 @@ func Difference(items ...interface{}) (diff interface{}, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReflectFieldMethodInterface parses intf attepting to return the field value or error otherwise
|
||||
// Supports "ExtraFields" where additional fields are dynamically inserted in map with field name: extraFieldsLabel
|
||||
func ReflectFieldMethodInterface(obj interface{}, fldName string) (retIf interface{}, err error) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
var field reflect.Value
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
field = v.FieldByName(fldName)
|
||||
case reflect.Map:
|
||||
field = v.MapIndex(reflect.ValueOf(fldName))
|
||||
case reflect.Slice, reflect.Array:
|
||||
//convert fldName to int
|
||||
idx, err := strconv.Atoi(fldName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if idx > v.Len() {
|
||||
return nil, fmt.Errorf("out of range")
|
||||
}
|
||||
field = v.Index(idx)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported field kind: %v", v.Kind())
|
||||
}
|
||||
if !field.IsValid() {
|
||||
// handle function with pointer
|
||||
v = reflect.ValueOf(obj)
|
||||
field = v.MethodByName(fldName)
|
||||
if !field.IsValid() {
|
||||
return nil, ErrNotFound
|
||||
} else {
|
||||
if field.Type().NumIn() != 0 {
|
||||
return nil, fmt.Errorf("invalid function called")
|
||||
}
|
||||
if field.Type().NumOut() > 2 {
|
||||
return nil, fmt.Errorf("invalid function called")
|
||||
}
|
||||
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if !field.Type().Out(1).Implements(errorInterface) {
|
||||
return nil, fmt.Errorf("invalid function called")
|
||||
}
|
||||
fields := field.Call([]reflect.Value{})
|
||||
//verify if error is not nil
|
||||
return fields[0].Interface(), nil
|
||||
}
|
||||
}
|
||||
return field.Interface(), nil
|
||||
}
|
||||
|
||||
@@ -694,3 +694,48 @@ func TestEqualTo(t *testing.T) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
}
|
||||
|
||||
type TestA struct {
|
||||
StrField string
|
||||
}
|
||||
|
||||
func (_ *TestA) TestFunc() string {
|
||||
return "This is a test function on a structure"
|
||||
}
|
||||
|
||||
func (_ *TestA) TestFuncWithParam(param string) string {
|
||||
return "Invalid"
|
||||
}
|
||||
|
||||
func (_ *TestA) TestFuncWithError() (string, error) {
|
||||
return "TestFunction", nil
|
||||
}
|
||||
func (_ *TestA) TestFuncWithError2() (string, error) {
|
||||
return "TestFunction", ErrPartiallyExecuted
|
||||
}
|
||||
|
||||
func TestReflectFieldMethodInterface(t *testing.T) {
|
||||
a := &TestA{StrField: "TestStructField"}
|
||||
ifValue, err := ReflectFieldMethodInterface(a, "StrField")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if ifValue != "TestStructField" {
|
||||
t.Errorf("Expecting: TestStructField, received: %+v", ifValue)
|
||||
}
|
||||
ifValue, err = ReflectFieldMethodInterface(a, "InexistentField")
|
||||
if err != ErrNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
ifValue, err = ReflectFieldMethodInterface(a, "TestFunc")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if ifValue != "This is a test function on a structure" {
|
||||
t.Errorf("Expecting: This is a test function on a structure, received: %+v", ifValue)
|
||||
}
|
||||
ifValue, err = ReflectFieldMethodInterface(a, "TestFuncWithError")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if ifValue != "TestFunction" {
|
||||
t.Errorf("Expecting: TestFunction, received: %+v", ifValue)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user