mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Updated NavigableMap FieldAsInterface
This commit is contained in:
committed by
Dan Christian Bogos
parent
d51fdaf90a
commit
1db551c932
@@ -23,6 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -101,54 +102,125 @@ func (nM *NavigableMap) FieldAsInterface(fldPath []string) (fldVal interface{},
|
||||
return nil, errors.New("empty field path")
|
||||
}
|
||||
lastMp := nM.data // last map when layered
|
||||
var canCast bool
|
||||
for i, spath := range fldPath {
|
||||
if i == lenPath-1 { // lastElement
|
||||
var idx *int
|
||||
if idxStart := strings.Index(spath, utils.IdxStart); idxStart != -1 &&
|
||||
strings.HasSuffix(spath, utils.IdxEnd) {
|
||||
slctr := spath[idxStart+1 : len(spath)-1]
|
||||
if !strings.HasPrefix(slctr, utils.DynamicDataPrefix) {
|
||||
if idxVal, err := strconv.Atoi(slctr); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
idx = utils.IntPointer(idxVal)
|
||||
}
|
||||
}
|
||||
spath = spath[:idxStart] // ignore the selector for now since it is processed in other places
|
||||
}
|
||||
var has bool
|
||||
fldVal, has = lastMp[spath]
|
||||
if !has {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
if valItms, isItms := fldVal.([]*NMItem); isItms && idx != nil {
|
||||
if *idx >= len(valItms) {
|
||||
return nil, fmt.Errorf("selector index %d out of range", *idx)
|
||||
}
|
||||
fldVal = valItms[*idx].Data
|
||||
}
|
||||
return nM.getLastItem(lastMp, spath)
|
||||
}
|
||||
if lastMp, err = nM.getNextMap(lastMp, spath); err != nil {
|
||||
return
|
||||
}
|
||||
elmnt, has := lastMp[spath]
|
||||
if !has {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
lastMp, canCast = elmnt.(map[string]interface{})
|
||||
if !canCast {
|
||||
lastMpNM, canCast := elmnt.(*NavigableMap) // attempt to cast into NavigableMap
|
||||
if !canCast {
|
||||
err = fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
|
||||
elmnt, elmnt, spath)
|
||||
return
|
||||
}
|
||||
lastMp = lastMpNM.data
|
||||
}
|
||||
}
|
||||
err = errors.New("end of function")
|
||||
return
|
||||
}
|
||||
|
||||
// getLastItem returns the item from the map
|
||||
// checking if it needs to return the item or an element of him if the item is a slice
|
||||
func (nM *NavigableMap) getLastItem(mp map[string]interface{}, spath string) (val interface{}, err error) {
|
||||
var idx *int
|
||||
spath, idx = nM.getIndex(spath)
|
||||
var has bool
|
||||
val, has = mp[spath]
|
||||
if !has {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
if idx == nil {
|
||||
return val, nil
|
||||
}
|
||||
switch vt := val.(type) {
|
||||
case []string:
|
||||
if *idx > len(vt) {
|
||||
return nil, utils.ErrNotFound
|
||||
// return nil, fmt.Errorf("selector index %d out of range", *idx)
|
||||
}
|
||||
return vt[*idx], nil
|
||||
case []*NMItem:
|
||||
if *idx > len(vt) {
|
||||
return nil, utils.ErrNotFound
|
||||
// return nil, fmt.Errorf("selector index %d out of range", *idx)
|
||||
}
|
||||
return vt[*idx].Data, nil
|
||||
default:
|
||||
}
|
||||
// only if all above fails use reflect:
|
||||
vr := reflect.ValueOf(val)
|
||||
if vr.Kind() == reflect.Ptr {
|
||||
vr = vr.Elem()
|
||||
}
|
||||
if vr.Kind() != reflect.Slice && vr.Kind() != reflect.Array {
|
||||
return nil, utils.ErrNotFound
|
||||
// return nil, fmt.Errorf("selector index used on non slice type(%T)", val)
|
||||
}
|
||||
if *idx > vr.Len() {
|
||||
return nil, utils.ErrNotFound
|
||||
// return nil, fmt.Errorf("selector index %d out of range", *idx)
|
||||
}
|
||||
return vr.Index(*idx).Interface(), nil
|
||||
}
|
||||
|
||||
// getNextMap returns the next map from the given map
|
||||
// used only for path parsing
|
||||
func (nM *NavigableMap) getNextMap(mp map[string]interface{}, spath string) (map[string]interface{}, error) {
|
||||
var idx *int
|
||||
spath, idx = nM.getIndex(spath)
|
||||
mi, has := mp[spath]
|
||||
if !has {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
if idx == nil {
|
||||
switch mv := mi.(type) {
|
||||
case map[string]interface{}:
|
||||
return mv, nil
|
||||
case *map[string]interface{}:
|
||||
return *mv, nil
|
||||
case NavigableMap:
|
||||
return mv.data, nil
|
||||
case *NavigableMap:
|
||||
return mv.data, nil
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
switch mv := mi.(type) {
|
||||
case []map[string]interface{}:
|
||||
if *idx < len(mv) {
|
||||
return mv[*idx], nil
|
||||
}
|
||||
case []NavigableMap:
|
||||
if *idx < len(mv) {
|
||||
return mv[*idx].data, nil
|
||||
}
|
||||
case []*NavigableMap:
|
||||
if *idx < len(mv) {
|
||||
return mv[*idx].data, nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
return nil, utils.ErrNotFound // xml compatible
|
||||
}
|
||||
return nil, fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
|
||||
mi, mi, spath)
|
||||
}
|
||||
|
||||
// getIndex returns the path and index if index present
|
||||
// path[index]=>path,index
|
||||
// path=>path,nil
|
||||
func (nM *NavigableMap) getIndex(spath string) (opath string, idx *int) {
|
||||
idxStart := strings.Index(spath, utils.IdxStart)
|
||||
if idxStart == -1 || !strings.HasSuffix(spath, utils.IdxEnd) {
|
||||
return spath, nil
|
||||
}
|
||||
slctr := spath[idxStart+1 : len(spath)-1]
|
||||
opath = spath[:idxStart]
|
||||
if strings.HasPrefix(slctr, utils.DynamicDataPrefix) {
|
||||
return
|
||||
}
|
||||
idxVal, err := strconv.Atoi(slctr)
|
||||
if err != nil {
|
||||
return spath, nil
|
||||
}
|
||||
return opath, &idxVal
|
||||
}
|
||||
|
||||
// FieldAsString returns the field value as string for the path specified
|
||||
// implements DataProvider
|
||||
func (nM *NavigableMap) FieldAsString(fldPath []string) (fldVal string, err error) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -873,3 +874,185 @@ func TestNavMapMerge(t *testing.T) {
|
||||
t.Errorf("expecting: %+v, received: %+v", nM2, nM)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavMapGetIndex(t *testing.T) {
|
||||
nM := NewNavigableMap(nil)
|
||||
var expIndx *int
|
||||
var expPath string
|
||||
var path string = "Fld1"
|
||||
expPath = path
|
||||
if rplyPath, rplyIndx := nM.getIndex(path); rplyPath != expPath && rplyIndx != expIndx {
|
||||
t.Errorf("Expected: path=%s ,indx=%v, received: path=%s ,indx=%v", expPath, expIndx, rplyPath, rplyIndx)
|
||||
}
|
||||
|
||||
path = "slice[5]"
|
||||
expPath = "slice"
|
||||
expIndx = utils.IntPointer(5)
|
||||
if rplyPath, rplyIndx := nM.getIndex(path); rplyPath != expPath && rplyIndx != expIndx {
|
||||
t.Errorf("Expected: path=%s ,indx=%v, received: path=%s ,indx=%v", expPath, expIndx, rplyPath, rplyIndx)
|
||||
}
|
||||
|
||||
path = "slice[~cgreq.Count]"
|
||||
expPath = "slice"
|
||||
expIndx = nil
|
||||
if rplyPath, rplyIndx := nM.getIndex(path); rplyPath != expPath && rplyIndx != expIndx {
|
||||
t.Errorf("Expected: path=%s ,indx=%v, received: path=%s ,indx=%v", expPath, expIndx, rplyPath, rplyIndx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavMapGetNextMap(t *testing.T) {
|
||||
nM := NewNavigableMap(nil)
|
||||
mp := map[string]interface{}{
|
||||
"field1": 10,
|
||||
"field2": []string{"val1", "val2"},
|
||||
"field3": []int{1, 2, 3},
|
||||
"map1": map[string]interface{}{"field1": 100000},
|
||||
"map2": []map[string]interface{}{map[string]interface{}{"field2": 11}},
|
||||
"map3": []NavigableMap{NavigableMap{data: map[string]interface{}{"field4": 112}}},
|
||||
}
|
||||
path := "map4"
|
||||
if _, err := nM.getNextMap(mp, path); err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
t.Errorf("Expected error: %s , received error %v", utils.ErrNotFound.Error(), err)
|
||||
}
|
||||
path = "map2[10]"
|
||||
if _, err := nM.getNextMap(mp, path); err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
t.Errorf("Expected error: %s , received error %v", utils.ErrNotFound.Error(), err)
|
||||
}
|
||||
path = "field1"
|
||||
experr := fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
|
||||
mp[path], mp[path], path)
|
||||
if _, err := nM.getNextMap(mp, path); err != nil && err.Error() != experr.Error() {
|
||||
t.Errorf("Expected error: %s , received error %v", experr.Error(), err)
|
||||
}
|
||||
path = "map1"
|
||||
expMap := map[string]interface{}{"field1": 100000}
|
||||
if rm, err := nM.getNextMap(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rm, expMap) {
|
||||
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expMap), utils.ToJSON(rm))
|
||||
}
|
||||
path = "map2[0]"
|
||||
expMap = map[string]interface{}{"field2": 11}
|
||||
if rm, err := nM.getNextMap(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rm, expMap) {
|
||||
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expMap), utils.ToJSON(rm))
|
||||
}
|
||||
path = "map3[0]"
|
||||
expMap = map[string]interface{}{"field4": 112}
|
||||
if rm, err := nM.getNextMap(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rm, expMap) {
|
||||
t.Errorf("Expected: %s, received: %s", utils.ToJSON(expMap), utils.ToJSON(rm))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavMapgetLastItem(t *testing.T) {
|
||||
nM := NewNavigableMap(nil)
|
||||
mp := map[string]interface{}{
|
||||
"field1": 10,
|
||||
"field2": []string{"val1", "val2"},
|
||||
"field3": []int{1, 2, 3},
|
||||
"map1": map[string]interface{}{"field1": 100000},
|
||||
"map2": []map[string]interface{}{map[string]interface{}{"field2": 11}},
|
||||
"map3": []NavigableMap{NavigableMap{data: map[string]interface{}{"field4": 112}}},
|
||||
}
|
||||
path := "map4"
|
||||
if _, err := nM.getLastItem(mp, path); err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
t.Errorf("Expected error: %s , received error %v", utils.ErrNotFound.Error(), err)
|
||||
}
|
||||
path = "map2[10]"
|
||||
if _, err := nM.getLastItem(mp, path); err != nil && err.Error() != utils.ErrNotFound.Error() {
|
||||
t.Errorf("Expected error: %s , received error %v", utils.ErrNotFound.Error(), err)
|
||||
}
|
||||
path = "field1"
|
||||
var expVal interface{} = 10
|
||||
if rplyVal, err := nM.getLastItem(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expVal, rplyVal) {
|
||||
t.Errorf("Expected: %v ,received: %v", expVal, rplyVal)
|
||||
}
|
||||
|
||||
path = "field2[1]"
|
||||
expVal = "val2"
|
||||
if rplyVal, err := nM.getLastItem(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expVal, rplyVal) {
|
||||
t.Errorf("Expected: %v ,received: %v", expVal, rplyVal)
|
||||
}
|
||||
path = "field3[2]"
|
||||
expVal = 3
|
||||
if rplyVal, err := nM.getLastItem(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expVal, rplyVal) {
|
||||
t.Errorf("Expected: %v ,received: %v", expVal, rplyVal)
|
||||
}
|
||||
path = "field2"
|
||||
expVal = []string{"val1", "val2"}
|
||||
if rplyVal, err := nM.getLastItem(mp, path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expVal, rplyVal) {
|
||||
t.Errorf("Expected: %v ,received: %v", expVal, rplyVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavMapFieldAsInterface(t *testing.T) {
|
||||
nM := &NavigableMap{
|
||||
data: map[string]interface{}{
|
||||
"FirstLevel": map[string]interface{}{
|
||||
"SecondLevel": []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"ThirdLevel": map[string]interface{}{
|
||||
"Fld1": "Val1",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"Count": 10,
|
||||
"ThirdLevel2": map[string]interface{}{
|
||||
"Fld2": []string{"Val1", "Val2", "Val3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"AnotherFirstLevel": "ValAnotherFirstLevel",
|
||||
},
|
||||
}
|
||||
|
||||
path := []string{"FirstLevel", "SecondLevel[0]", "Count"}
|
||||
expErr := utils.ErrNotFound
|
||||
var eVal interface{} = nil
|
||||
if _, err := nM.FieldAsInterface(path); err != nil && err.Error() != expErr.Error() {
|
||||
t.Errorf("Expected error: %s, received error: %v", expErr.Error(), err)
|
||||
}
|
||||
|
||||
path = []string{"AnotherFirstLevel", "SecondLevel", "Count"}
|
||||
expErr = fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
|
||||
nM.data["AnotherFirstLevel"], nM.data["AnotherFirstLevel"], "AnotherFirstLevel")
|
||||
if _, err := nM.FieldAsInterface(path); err != nil && err.Error() != expErr.Error() {
|
||||
t.Errorf("Expected error: %s, received error: %v", expErr.Error(), err)
|
||||
}
|
||||
|
||||
path = []string{"FirstLevel", "SecondLevel[1]", "Count"}
|
||||
eVal = 10
|
||||
if rplyVal, err := nM.FieldAsInterface(path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eVal, rplyVal) {
|
||||
t.Errorf("Expected: %s , received: %s", utils.ToJSON(eVal), utils.ToJSON(rplyVal))
|
||||
}
|
||||
|
||||
path = []string{"FirstLevel", "SecondLevel[1]", "ThirdLevel2", "Fld2"}
|
||||
eVal = []string{"Val1", "Val2", "Val3"}
|
||||
if rplyVal, err := nM.FieldAsInterface(path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eVal, rplyVal) {
|
||||
t.Errorf("Expected: %s , received: %s", utils.ToJSON(eVal), utils.ToJSON(rplyVal))
|
||||
}
|
||||
|
||||
path = []string{"FirstLevel", "SecondLevel[1]", "ThirdLevel2", "Fld2[2]"}
|
||||
eVal = "Val3"
|
||||
if rplyVal, err := nM.FieldAsInterface(path); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eVal, rplyVal) {
|
||||
t.Errorf("Expected: %s , received: %s", utils.ToJSON(eVal), utils.ToJSON(rplyVal))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
@@ -148,6 +149,24 @@ func (sSpls *SortedSuppliers) Digest() string {
|
||||
return strings.Join(sSpls.SuppliersWithParams(), utils.FIELDS_SEP)
|
||||
}
|
||||
|
||||
func (sSpls *SortedSuppliers) AsNavigableMap() (nm *config.NavigableMap) {
|
||||
mp := map[string]interface{}{
|
||||
"ProfileID": sSpls.ProfileID,
|
||||
"Sorting": sSpls.Sorting,
|
||||
"Count": sSpls.Count,
|
||||
}
|
||||
sm := make([]map[string]interface{}, len(sSpls.SortedSuppliers))
|
||||
for i, ss := range sSpls.SortedSuppliers {
|
||||
sm[i] = map[string]interface{}{
|
||||
"SupplierID": ss.SupplierID,
|
||||
"SupplierParameters": ss.SupplierParameters,
|
||||
"SortingData": ss.SortingData,
|
||||
}
|
||||
}
|
||||
mp["SortedSuppliers"] = sm
|
||||
return config.NewNavigableMap(mp)
|
||||
}
|
||||
|
||||
type SupplierWithParams struct {
|
||||
SupplierName string
|
||||
SupplierParams string
|
||||
|
||||
@@ -1714,7 +1714,7 @@ func (v1AuthReply *V1AuthorizeReply) AsNavigableMap(
|
||||
cgrReply[utils.CapMaxUsage] = *v1AuthReply.MaxUsage
|
||||
}
|
||||
if v1AuthReply.Suppliers != nil {
|
||||
cgrReply[utils.CapSuppliers] = *v1AuthReply.Suppliers
|
||||
cgrReply[utils.CapSuppliers] = v1AuthReply.Suppliers.AsNavigableMap()
|
||||
}
|
||||
if v1AuthReply.ThresholdIDs != nil {
|
||||
cgrReply[utils.CapThresholds] = *v1AuthReply.ThresholdIDs
|
||||
@@ -2604,7 +2604,7 @@ func (v1Rply *V1ProcessEventReply) AsNavigableMap(
|
||||
cgrReply[utils.CapAttributes] = attrs
|
||||
}
|
||||
if v1Rply.Suppliers != nil {
|
||||
cgrReply[utils.CapSuppliers] = *v1Rply.Suppliers
|
||||
cgrReply[utils.CapSuppliers] = v1Rply.Suppliers.AsNavigableMap()
|
||||
}
|
||||
}
|
||||
return config.NewNavigableMap(cgrReply), nil
|
||||
|
||||
@@ -1007,7 +1007,7 @@ func TestSessionSV1AuthorizeReplyAsNavigableMap(t *testing.T) {
|
||||
utils.CapAttributes: map[string]interface{}{"OfficeGroup": "Marketing"},
|
||||
utils.CapResourceAllocation: "ResGr1",
|
||||
utils.CapMaxUsage: 5 * time.Minute,
|
||||
utils.CapSuppliers: *splrs,
|
||||
utils.CapSuppliers: splrs.AsNavigableMap(),
|
||||
utils.CapThresholds: *thIDs,
|
||||
utils.CapStatQueues: *statIDs,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user