diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go
index 05b7f4d4e..199bc74a0 100644
--- a/agents/agentreq_test.go
+++ b/agents/agentreq_test.go
@@ -145,10 +145,10 @@ func TestAgentRequestSetFields(t *testing.T) {
cfg, _ := config.NewDefaultCGRConfig()
dm := engine.NewDataManager(engine.NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items),
config.CgrConfig().CacheCfg(), nil)
- ar := NewAgentRequest(config.NewNavigableMap(req), nil,
+ ar := NewAgentRequest(utils.MapStorage(req), nil,
nil, nil, nil, config.NewRSRParsersMustCompile("", false, utils.NestingSep),
"cgrates.org", "", engine.NewFilterS(cfg, nil, dm),
- config.NewNavigableMap(req), config.NewNavigableMap(req))
+ utils.MapStorage(req), utils.MapStorage(req))
input := []*config.FCTemplate{}
if err := ar.SetFields(input); err != nil {
t.Error(err)
diff --git a/agents/diamagent_test.go b/agents/diamagent_test.go
index 01f6ce046..2ba06c95d 100644
--- a/agents/diamagent_test.go
+++ b/agents/diamagent_test.go
@@ -62,12 +62,12 @@ func TestProcessRequest(t *testing.T) {
cgrRplyNM := utils.NavigableMap2{}
rply := utils.NewOrderedNavigableMap()
- diamDP := config.NewNavigableMap(map[string]interface{}{
+ diamDP := utils.MapStorage{
"SessionId": "123456",
"Account": "1001",
"Destination": "1003",
"Usage": 10 * time.Second,
- })
+ }
reqProcessor := &config.RequestProcessor{
ID: "Default",
Tenant: config.NewRSRParsersMustCompile("cgrates.org", true, utils.INFIELD_SEP),
diff --git a/agents/libdiam.go b/agents/libdiam.go
index 522750f36..e73ca5fd7 100644
--- a/agents/libdiam.go
+++ b/agents/libdiam.go
@@ -289,7 +289,7 @@ func writeOnConn(c diam.Conn, m *diam.Message) (err error) {
// newDADataProvider constructs a DataProvider for a diameter message
func newDADataProvider(c diam.Conn, m *diam.Message) utils.DataProvider {
- return &diameterDP{c: c, m: m, cache: config.NewNavigableMap(nil)}
+ return &diameterDP{c: c, m: m, cache: utils.MapStorage{}}
}
@@ -298,7 +298,7 @@ func newDADataProvider(c diam.Conn, m *diam.Message) utils.DataProvider {
type diameterDP struct {
c diam.Conn
m *diam.Message
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -408,7 +408,7 @@ func (dP *diameterDP) FieldAsInterface(fldPath []string) (data interface{}, err
if data, err = diamAVPAsIface(avps[slectedIdx]); err != nil {
return nil, err
}
- dP.cache.Set(fldPath, data, false, false)
+ dP.cache.Set(fldPath, data)
return
}
diff --git a/agents/libdns.go b/agents/libdns.go
index 147a914dc..629b05a0d 100644
--- a/agents/libdns.go
+++ b/agents/libdns.go
@@ -62,7 +62,7 @@ func domainNameFromNAPTR(name string) (dName string) {
func newDNSDataProvider(req *dns.Msg,
w dns.ResponseWriter) utils.DataProvider {
return &dnsDP{req: req, w: w,
- cache: config.NewNavigableMap(nil)}
+ cache: utils.MapStorage{}}
}
// dnsDP implements engien.DataProvider, serving as dns.Msg decoder
@@ -70,7 +70,7 @@ func newDNSDataProvider(req *dns.Msg,
type dnsDP struct {
req *dns.Msg
w dns.ResponseWriter
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -109,7 +109,7 @@ func (dP *dnsDP) FieldAsInterface(fldPath []string) (data interface{}, err error
if len(dP.req.Question) != 0 {
data = dP.req.Question[0]
}
- dP.cache.Set(fldPath, data, false, false)
+ dP.cache.Set(fldPath, data)
return
}
diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go
index be5c44160..18af2e458 100644
--- a/agents/libhttpagent.go
+++ b/agents/libhttpagent.go
@@ -48,7 +48,7 @@ func newHADataProvider(reqPayload string,
}
func newHTTPUrlDP(req *http.Request) (dP utils.DataProvider, err error) {
- dP = &httpUrlDP{req: req, cache: config.NewNavigableMap(nil)}
+ dP = &httpUrlDP{req: req, cache: utils.MapStorage{}}
return
}
@@ -56,7 +56,7 @@ func newHTTPUrlDP(req *http.Request) (dP utils.DataProvider, err error) {
// decoded data is only searched once and cached
type httpUrlDP struct {
req *http.Request
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -80,7 +80,7 @@ func (hU *httpUrlDP) FieldAsInterface(fldPath []string) (data interface{}, err e
return // data found in cache
}
data = hU.req.FormValue(fldPath[0])
- hU.cache.Set(fldPath, data, false, false)
+ hU.cache.Set(fldPath, data)
return
}
@@ -109,14 +109,14 @@ func newHTTPXmlDP(req *http.Request) (dP utils.DataProvider, err error) {
if err != nil {
return nil, err
}
- dP = &httpXmlDP{xmlDoc: doc, cache: config.NewNavigableMap(nil), addr: req.RemoteAddr}
+ dP = &httpXmlDP{xmlDoc: doc, cache: utils.MapStorage{}, addr: req.RemoteAddr}
return
}
// httpXmlDP implements utils.DataProvider, serving as xml data decoder
// decoded data is only searched once and cached
type httpXmlDP struct {
- cache *config.NavigableMap
+ cache utils.MapStorage
xmlDoc *xmlquery.Node
addr string
}
@@ -168,7 +168,7 @@ func (hU *httpXmlDP) FieldAsInterface(fldPath []string) (data interface{}, err e
}
//add the content in data and cache it
data = elmnt.InnerText()
- hU.cache.Set(fldPath, data, false, false)
+ hU.cache.Set(fldPath, data)
return
}
diff --git a/agents/librad.go b/agents/librad.go
index 41b51bdda..bb740f850 100644
--- a/agents/librad.go
+++ b/agents/librad.go
@@ -62,7 +62,7 @@ func radReplyAppendAttributes(reply *radigo.Packet, rplNM *utils.OrderedNavigabl
// newRADataProvider constructs a DataProvider
func newRADataProvider(req *radigo.Packet) (dP utils.DataProvider) {
- dP = &radiusDP{req: req, cache: config.NewNavigableMap(nil)}
+ dP = &radiusDP{req: req, cache: utils.MapStorage{}}
return
}
@@ -70,7 +70,7 @@ func newRADataProvider(req *radigo.Packet) (dP utils.DataProvider) {
// decoded data is only searched once and cached
type radiusDP struct {
req *radigo.Packet
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -95,7 +95,7 @@ func (pk *radiusDP) FieldAsInterface(fldPath []string) (data interface{}, err er
if len(pk.req.AttributesWithName(fldPath[0], "")) != 0 {
data = pk.req.AttributesWithName(fldPath[0], "")[0].GetStringValue()
}
- pk.cache.Set(fldPath, data, false, false)
+ pk.cache.Set(fldPath, data)
return
}
diff --git a/agents/sipagent.go b/agents/sipagent.go
index 4ef7209d0..4aafacf20 100644
--- a/agents/sipagent.go
+++ b/agents/sipagent.go
@@ -227,7 +227,7 @@ func (sa *SIPAgent) handleMessage(sipMessage sipingo.Message, remoteHost string)
for k, v := range sipMessage {
sipMessageIface[k] = v
}
- dp := config.NewNavigableMap(sipMessageIface)
+ dp := utils.MapStorage(sipMessageIface)
var processed bool
cgrRplyNM := utils.NavigableMap2{}
rplyNM := utils.NewOrderedNavigableMap()
diff --git a/config/fwvdp.go b/config/fwvdp.go
index 1ab306333..61e332b23 100644
--- a/config/fwvdp.go
+++ b/config/fwvdp.go
@@ -29,14 +29,14 @@ import (
// NewfwvProvider constructs a utils.DataProvider
func NewFWVProvider(record string) (dP utils.DataProvider) {
- dP = &FWVProvider{req: record, cache: NewNavigableMap(nil)}
+ dP = &FWVProvider{req: record, cache: utils.MapStorage{}}
return
}
// fwvProvider implements engine.utils.DataProvider so we can pass it to filters
type FWVProvider struct {
req string
- cache *NavigableMap
+ cache utils.MapStorage
}
// String is part of engine.utils.DataProvider interface
@@ -75,7 +75,7 @@ func (fP *FWVProvider) FieldAsInterface(fldPath []string) (data interface{}, err
return "", fmt.Errorf("FinalIndex : %+v is greater than : %+v", finalIndex, len(fP.req))
}
data = fP.req[startIndex:finalIndex]
- fP.cache.Set(fldPath, data, false, false)
+ fP.cache.Set(fldPath, data)
return
}
diff --git a/config/navigablemap.go b/config/navigablemap.go
index 499f35744..716a3bfe8 100644
--- a/config/navigablemap.go
+++ b/config/navigablemap.go
@@ -20,11 +20,7 @@ package config
import (
"encoding/xml"
- "errors"
"fmt"
- "net"
- "reflect"
- "strconv"
"strings"
"time"
@@ -83,427 +79,6 @@ func (nmi *NMItem) Len() int {
return 0
}
-// NewNavigableMap constructs a NavigableMap
-func NewNavigableMap(data map[string]interface{}) *NavigableMap {
- if data == nil {
- data = make(map[string]interface{})
- }
- return &NavigableMap{data: data}
-}
-
-// NavigableMap is a map who's values can be navigated via path
-// data can be retrieved as ordered
-// NavigableMap is not thread safe due to performance demands, could come back if needed
-type NavigableMap struct {
- data map[string]interface{} // layered map
- order [][]string // order of field paths
-}
-
-// Set will set items into NavigableMap populating also order
-// apnd parameter allows appending the data if both sides are []*NMItem
-func (nM *NavigableMap) Set(path []string, data interface{}, apnd, ordered bool) {
- if ordered {
- nM.order = append(nM.order, path)
- }
- mp := nM.data
- for i, spath := range path {
- if i == len(path)-1 { // last path
- oData, has := mp[spath]
- if !has || !apnd { // no need to append
- mp[spath] = data
- return
- }
- dataItms, isNMItems := data.([]*NMItem)
- if !isNMItems { // new data is not items
- mp[spath] = data
- return
- }
- oItms, isNMItems := oData.([]*NMItem)
- if !isNMItems { // previous data is not items, simply overwrite
- mp[spath] = data
- return
- }
- mp[spath] = append(oItms, dataItms...)
- return
- }
- if _, has := mp[spath]; !has {
- mp[spath] = make(map[string]interface{})
- }
- mp = mp[spath].(map[string]interface{}) // so we can check further down
- }
-}
-
-// GetField returns a field in it's original format, without converting from ie. *NMItem
-func (nM *NavigableMap) GetField(path []string) (fldVal interface{}, err error) {
- lenPath := len(path)
- if lenPath == 0 {
- return nil, errors.New("empty field path")
- }
- lastMp := nM.data // last map when layered
- for i, spath := range path {
- if i == lenPath-1 { // lastElement
- return nM.getLastRealItem(lastMp, spath)
- }
- var dp interface{}
- if dp, err = nM.getNextMap(lastMp, spath); err != nil {
- return
- }
- switch mv := dp.(type) { // used for cdr when populating eventCost whitin
- case map[string]interface{}:
- lastMp = mv
- case utils.DataProvider:
- return mv.FieldAsInterface(path[i+1:])
- default:
- return nil, fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
- dp, dp, spath)
- }
- }
- panic("BUG") // should never make it here
-}
-
-// 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) getLastRealItem(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, fmt.Errorf("selector index %d out of range", *idx)
- }
- return vt[*idx], 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, fmt.Errorf("selector index used on non slice type(%T)", val)
- }
- if *idx > vr.Len() {
- return nil, fmt.Errorf("selector index %d out of range", *idx)
- }
- return vr.Index(*idx).Interface(), nil
-}
-
-// FieldAsInterface returns the field value as interface{} for the path specified
-// implements utils.DataProvider
-// supports spath with selective elements in case of []*NMItem
-func (nM *NavigableMap) FieldAsInterface(fldPath []string) (fldVal interface{}, err error) {
- lenPath := len(fldPath)
- if lenPath == 0 {
- return nil, errors.New("empty field path")
- }
- lastMp := nM.data // last map when layered
- for i, spath := range fldPath {
- if i == lenPath-1 { // lastElement
- return nM.getLastItem(lastMp, spath)
- }
- var dp interface{}
- if dp, err = nM.getNextMap(lastMp, spath); err != nil {
- return
- }
- switch mv := dp.(type) { // used for cdr when populating eventCost whitin
- case map[string]interface{}:
- lastMp = mv
- case utils.DataProvider:
- return mv.FieldAsInterface(fldPath[i+1:])
- default:
- return nil, fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}",
- dp, dp, spath)
- }
- }
- 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) (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
- case utils.DataProvider: // used for cdr when populating eventCost whitin
- return mv, nil
- default:
- }
- } else {
- switch mv := mi.(type) {
- case []interface{}:
- // in case we create the map using json and we marshall the value into a map[string]interface{}
- // we can have slice of interfaces that is masking a slice of map[string]interface{}
- // this is for CostDetails BalanceSummaries
- if *idx < len(mv) {
- mm := mv[*idx]
- switch mmv := mm.(type) {
- case map[string]interface{}:
- return mmv, nil
- case *map[string]interface{}:
- return *mmv, nil
- case NavigableMap:
- return mmv.data, nil
- case *NavigableMap:
- return mmv.data, nil
- default:
- }
- }
- 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
- }
- case []utils.DataProvider: // used for cdr when populating eventCost whitin
- if *idx < len(mv) {
- return mv[*idx], 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 utils.DataProvider
-func (nM *NavigableMap) FieldAsString(fldPath []string) (fldVal string, err error) {
- var valIface interface{}
- valIface, err = nM.FieldAsInterface(fldPath)
- if err != nil {
- return
- }
- return utils.IfaceAsString(valIface), nil
-}
-
-// String is part of engine.utils.DataProvider interface
-func (nM *NavigableMap) String() string {
- return utils.ToJSON(nM.data)
-}
-
-// RemoteHost is part of engine.utils.DataProvider interface
-func (nM *NavigableMap) RemoteHost() net.Addr {
- return utils.LocalAddr()
-}
-
-// indexMapElements will recursively go through map and index the element paths into elmns
-func indexMapElements(mp map[string]interface{}, path []string, vals *[]interface{}) {
- for k, v := range mp {
- vPath := append(path, k)
- if mpIface, isMap := v.(map[string]interface{}); isMap {
- indexMapElements(mpIface, vPath, vals)
- continue
- }
- valsOut := append(*vals, v)
- *vals = valsOut
- }
-}
-
-// Values returns the values in map, ordered by order information
-func (nM *NavigableMap) Values() (vals []interface{}) {
- if len(nM.data) == 0 {
- return
- }
- if len(nM.order) == 0 {
- indexMapElements(nM.data, []string{}, &vals)
- return
- }
- vals = make([]interface{}, len(nM.order))
- for i, path := range nM.order {
- val, _ := nM.FieldAsInterface(path)
- vals[i] = val
- }
- return
-}
-
-// Merge will update nM with values from a second one
-func (nM *NavigableMap) Merge(nM2 *NavigableMap) {
- if nM2 == nil {
- return
- }
- if len(nM2.order) == 0 {
- indexMapPaths(nM2.data, nil, &nM.order)
- }
- pathIdx := make(map[string]int) // will hold references for last index exported in case of []*NMItem
- for _, path := range nM2.order {
- val, _ := nM2.FieldAsInterface(path)
- if valItms, isItms := val.([]*NMItem); isItms {
- pathStr := strings.Join(path, utils.NestingSep)
- pathIdx[pathStr]++
- if pathIdx[pathStr] > len(valItms) {
- val = valItms[len(valItms)-1:] // slice with only last element in, so we can set it unlimited
- } else {
- val = []*NMItem{valItms[pathIdx[pathStr]-1]} // set only one item per path
- }
- }
- nM.Set(path, val, true,
- (len(nM.order) != 0 || len(nM.data) == 0))
- }
- return
-}
-
-// indexMapPaths parses map returning the parsed branchPath, useful when not having order for NavigableMap
-func indexMapPaths(mp map[string]interface{}, branchPath []string, parsedPaths *[][]string) {
- for k, v := range mp {
- if mpIface, isMap := v.(map[string]interface{}); isMap {
- indexMapPaths(mpIface, append(branchPath, k), parsedPaths)
- continue
- }
- tmpPaths := append(*parsedPaths, append(branchPath, k))
- *parsedPaths = tmpPaths
- }
-}
-
-// AsCGREvent builds a CGREvent considering Time as time.Now()
-// and Event as linear map[string]interface{} with joined paths
-// treats particular case when the value of map is []*NMItem - used in agents/AgentRequest
-func (nM *NavigableMap) AsCGREvent(tnt string, pathSep string) (cgrEv *utils.CGREvent) {
- if nM == nil || len(nM.data) == 0 {
- return
- }
- cgrEv = &utils.CGREvent{
- Tenant: tnt,
- ID: utils.UUIDSha1Prefix(),
- Time: utils.TimePointer(time.Now()),
- Event: make(map[string]interface{})}
- if len(nM.order) == 0 {
- indexMapPaths(nM.data, nil, &nM.order)
- }
- for _, branchPath := range nM.order {
- val, _ := nM.FieldAsInterface(branchPath)
- if nmItms, isNMItems := val.([]*NMItem); isNMItems { // special case when we have added multiple items inside a key, used in agents
- for _, nmItm := range nmItms {
- if nmItm.Config == nil ||
- nmItm.Config.AttributeID == "" {
- val = nmItm.Data // first item which is not an attribute will become the value
- break
- }
- }
- }
- cgrEv.Event[strings.Join(branchPath, pathSep)] = val
- }
- return
-}
-
-// AsMapStringIface builds a linear map[string]interface{} with joined paths
-// treats particular case when the value of map is []*NMItem - used in agents/AgentRequest
-func (nM *NavigableMap) AsMapStringIface(pathSep string) (mp map[string]interface{}) {
- if nM == nil || len(nM.data) == 0 {
- return
- }
- mp = make(map[string]interface{})
- if len(nM.order) == 0 {
- indexMapPaths(nM.data, nil, &nM.order)
- }
- for _, branchPath := range nM.order {
- val, _ := nM.FieldAsInterface(branchPath)
- if nmItms, isNMItems := val.([]*NMItem); isNMItems { // special case when we have added multiple items inside a key, used in agents
- for _, nmItm := range nmItms {
- if nmItm.Config == nil ||
- nmItm.Config.AttributeID == "" {
- val = nmItm.Data // first item which is not an attribute will become the value
- break
- }
- }
- }
- mp[strings.Join(branchPath, pathSep)] = val
- }
- return
-}
-
// XMLElement is specially crafted to be automatically marshalled by encoding/xml
type XMLElement struct {
XMLName xml.Name
@@ -512,222 +87,6 @@ type XMLElement struct {
Elements []*XMLElement `xml:"omitempty"`
}
-// AsXMLElements returns the values as []*XMLElement which can be later marshaled
-// considers each value returned by .Values() in the form of []*NMItem, otherwise errors
-func (nM *NavigableMap) AsXMLElements() (ents []*XMLElement, err error) {
- pathIdx := make(map[string]*XMLElement) // Keep the index of elements based on path
- for _, val := range nM.Values() {
- nmItms, isNMItems := val.([]*NMItem)
- if !isNMItems {
- return nil, fmt.Errorf("value: %+v is not []*NMItem", val)
- }
- for _, nmItm := range nmItms {
- if nmItm.Config != nil && nmItm.Config.NewBranch {
- pathIdx = make(map[string]*XMLElement) // reset cache so we can start having other elements with same path
- }
- val := utils.IfaceAsString(nmItm.Data)
- var pathCached bool
- for i := len(nmItm.Path); i > 0; i-- {
- var cachedElm *XMLElement
- if cachedElm, pathCached = pathIdx[strings.Join(nmItm.Path[:i], "")]; !pathCached {
- continue
- }
- if i == len(nmItm.Path) { // lastElmnt, overwrite value or add attribute
- if nmItm.Config != nil &&
- nmItm.Config.AttributeID != "" {
- cachedElm.Attributes = append(cachedElm.Attributes,
- &xml.Attr{xml.Name{Local: nmItm.Config.AttributeID}, val})
- } else {
- cachedElm.Value = val
- }
- break
- }
- // create elements in reverse order so we can append already created
- var newElm *XMLElement
- for j := len(nmItm.Path); j > i; j-- {
- elm := &XMLElement{XMLName: xml.Name{Local: nmItm.Path[j-1]}}
- pathIdx[strings.Join(nmItm.Path[:j], "")] = elm
- if newElm == nil {
- if nmItm.Config != nil &&
- nmItm.Config.AttributeID != "" {
- elm.Attributes = append(elm.Attributes,
- &xml.Attr{xml.Name{Local: nmItm.Config.AttributeID}, val})
- } else {
- elm.Value = val
- }
- newElm = elm // last element
- } else {
- elm.Elements = append(elm.Elements, newElm)
- newElm = elm
- }
- }
- cachedElm.Elements = append(cachedElm.Elements, newElm)
- }
- if !pathCached { // not an update but new element to be created
- var newElm *XMLElement
- for i := len(nmItm.Path); i > 0; i-- {
- elm := &XMLElement{XMLName: xml.Name{Local: nmItm.Path[i-1]}}
- pathIdx[strings.Join(nmItm.Path[:i], "")] = elm
- if newElm == nil { // last element, create data inside
- if nmItm.Config != nil &&
- nmItm.Config.AttributeID != "" {
- elm.Attributes = append(elm.Attributes,
- &xml.Attr{xml.Name{Local: nmItm.Config.AttributeID}, val})
- } else {
- elm.Value = val
- }
- newElm = elm // last element
- } else {
- elm.Elements = append(elm.Elements, newElm)
- newElm = elm
- }
- }
- ents = append(ents, newElm)
- }
- }
- }
- return
-}
-
-func getPathFromValue(in reflect.Value, prefix string) (out []string) {
- switch in.Kind() {
- case reflect.Ptr:
- return getPathFromValue(in.Elem(), prefix)
- case reflect.Array, reflect.Slice:
- prefix = strings.TrimSuffix(prefix, utils.NestingSep)
- for i := 0; i < in.Len(); i++ {
- pref := fmt.Sprintf("%s[%v]", prefix, i)
- out = append(out, pref)
- out = append(out, getPathFromValue(in.Index(i), pref+utils.NestingSep)...)
- }
- case reflect.Map:
- iter := reflect.ValueOf(in).MapRange()
- for iter.Next() {
- pref := prefix + iter.Key().String()
- out = append(out, pref)
- out = append(out, getPathFromValue(iter.Value(), pref+utils.NestingSep)...)
- }
- case reflect.Struct:
- inType := in.Type()
- for i := 0; i < in.NumField(); i++ {
- pref := prefix + inType.Field(i).Name
- out = append(out, pref)
- out = append(out, getPathFromValue(in.Field(i), pref+utils.NestingSep)...)
- }
- case reflect.Invalid, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String, reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Interface:
- default:
- }
- return
-}
-
-func getPathFromInterface(in interface{}, prefix string) (out []string) {
- switch vin := in.(type) {
- case map[string]interface{}:
- for k, val := range vin {
- pref := prefix + k
- out = append(out, pref)
- out = append(out, getPathFromInterface(val, pref+utils.NestingSep)...)
- }
- case []map[string]interface{}:
- prefix = strings.TrimSuffix(prefix, utils.NestingSep)
- for i, val := range vin {
- pref := fmt.Sprintf("%s[%v]", prefix, i)
- out = append(out, pref)
- out = append(out, getPathFromInterface(val, pref+utils.NestingSep)...)
- }
- case []interface{}:
- prefix = strings.TrimSuffix(prefix, utils.NestingSep)
- for i, val := range vin {
- pref := fmt.Sprintf("%s[%v]", prefix, i)
- out = append(out, pref)
- out = append(out, getPathFromInterface(val, pref+utils.NestingSep)...)
- }
- case []string:
- prefix = strings.TrimSuffix(prefix, utils.NestingSep)
- for i := range vin {
- pref := fmt.Sprintf("%s[%v]", prefix, i)
- out = append(out, pref)
- }
- case nil, int, int32, int64, uint32, uint64, bool, float32, float64, []uint8, time.Duration, time.Time, string: //no path
- default: //reflect based
- out = getPathFromValue(reflect.ValueOf(vin), prefix)
- }
- return
-}
-
-// GetKeys returns all posibble keys
-func (nM *NavigableMap) GetKeys() (keys []string) {
- for k, val := range nM.data {
- keys = append(keys, k)
- keys = append(keys, getPathFromInterface(val, k+utils.NestingSep)...)
- }
- return
-}
-
-// Remove will remove the items from the given path
-func (nM *NavigableMap) Remove(path []string) {
- // if ordered {
- // nM.order = append(nM.order, path)
- // }
- mp := nM.data
- for i, spath := range path {
- oData, has := mp[spath]
- if !has { // no need to remove
- return
- }
- if i == len(path)-1 { // last path
- delete(mp, spath)
- return
- }
- defer func(np map[string]interface{}, p string) {
- o, has := np[p]
- if !has {
- return
- }
- if o == nil {
- delete(np, p)
- return
- }
- v, ok := o.(map[string]interface{})
- if !ok {
- return
- }
- if len(v) == 0 {
- delete(np, p)
- }
- }(mp, spath)
- mp = oData.(map[string]interface{}) // so we can check further down
- }
-}
-
-// RemoveAll will clean the data and the odrder from NavigableMap
-func (nM *NavigableMap) RemoveAll() {
- nM.data = make(map[string]interface{})
- nM.order = make([][]string, 0)
-}
-
-// GetData returns the map from inside the NavMap without modifying it
-func (nM *NavigableMap) GetData() (mp map[string]interface{}) {
- mp = make(map[string]interface{})
- if len(nM.order) == 0 {
- indexMapPaths(nM.data, nil, &nM.order)
- }
- for _, branchPath := range nM.order {
- val, _ := nM.FieldAsInterface(branchPath)
- if nmItms, isNMItems := val.([]*NMItem); isNMItems { // special case when we have added multiple items inside a key, used in agents
- for _, nmItm := range nmItms {
- if nmItm.Config == nil ||
- nmItm.Config.AttributeID == "" {
- val = nmItm.Data // first item which is not an attribute will become the value
- break
- }
- }
- }
- mp[strings.Join(branchPath, utils.NestingSep)] = val
- }
- return
-}
-
// NMAsXMLElements returns the values as []*XMLElement which can be later marshaled
// considers each value returned by .Values() in the form of []*NMItem, otherwise errors
func NMAsXMLElements(nm *utils.OrderedNavigableMap) (ents []*XMLElement, err error) {
diff --git a/config/navigablemap_test.go b/config/navigablemap_test.go
index f4f7b4439..75c42c56a 100644
--- a/config/navigablemap_test.go
+++ b/config/navigablemap_test.go
@@ -19,1171 +19,14 @@ package config
import (
"encoding/xml"
- "fmt"
"math/rand"
"reflect"
- "sort"
"strings"
"testing"
"github.com/cgrates/cgrates/utils"
)
-func TestNavMapGetFieldAsString(t *testing.T) {
- nM := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "AnotherFirstLevel": "ValAnotherFirstLevel",
- },
- }
- eVal := "Val1"
- if strVal, err := nM.FieldAsString(
- strings.Split("FirstLevel>SecondLevel>ThirdLevel>Fld1", ">")); err != nil {
- t.Error(err)
- } else if strVal != eVal {
- t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
- }
- eVal = "ValAnotherFirstLevel"
- if strVal, err := nM.FieldAsString(
- strings.Split("AnotherFirstLevel", ">")); err != nil {
- t.Error(err)
- } else if strVal != eVal {
- t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
- }
- fPath := "NonExisting>AnotherFirstLevel"
- if _, err := nM.FieldAsString(strings.Split(fPath, ">")); err.Error() !=
- utils.ErrNotFound.Error() {
- t.Error(err)
- }
-}
-
-type myEv map[string]interface{}
-
-func (ev myEv) AsNavigableMap() *NavigableMap {
- return NewNavigableMap(ev)
-}
-
-func TestNavMapAsNavigableMap(t *testing.T) {
- myData := myEv{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": 123.123,
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": 123,
- },
- "Field3": "Value3",
- },
- "Field4": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- }
-
- eNavMap := NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": 123.123,
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": 123,
- },
- "Field3": "Value3",
- },
- "Field4": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- },
- }
-
- if rcv := myData.AsNavigableMap(); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eNavMap.data, rcv.data) {
- t.Errorf("Expecting: %+v, received: %+v", eNavMap.data, rcv.data)
- }
-}
-
-func TestNavMapNewNavigableMap(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": "Value2",
- },
- "Field3": "Value3",
- },
- "Field4": "Val4",
- }
-
- eNavMap := NavigableMap{
- data: myData,
- }
-
- nM := NewNavigableMap(myData)
- if !reflect.DeepEqual(nM.data, eNavMap.data) {
- t.Errorf("Expecting: %+v, received: %+v", eNavMap.data, nM.data)
- }
-}
-
-func TestNavMapAdd(t *testing.T) {
- nM := NewNavigableMap(nil)
- path := []string{"FistLever2", "SecondLevel2", "Field2"}
- data := "Value2"
- nM.Set(path, data, false, true)
- path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}
- data = "Val1"
- nM.Set(path, data, false, true)
- path = []string{"FistLever2", "Field3"}
- data = "Value3"
- nM.Set(path, data, false, true)
- path = []string{"Field4"}
- data = "Val4"
- nM.Set(path, data, false, true)
- eNavMap := NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": "Value2",
- },
- "Field3": "Value3",
- },
- "Field4": "Val4",
- },
- }
- if !reflect.DeepEqual(nM.data, eNavMap.data) {
- t.Errorf("Expecting: %+v, received: %+v", eNavMap.data, nM.data)
- }
- eOrder := [][]string{
- []string{"FistLever2", "SecondLevel2", "Field2"},
- []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- []string{"FistLever2", "Field3"},
- []string{"Field4"},
- }
- if !reflect.DeepEqual(eOrder, nM.order) {
- t.Errorf("Expecting: %+v, received: %+v", eOrder, nM.order)
- }
-
-}
-
-type testStruct struct {
- Item1 string
- Item2 int
-}
-
-func TestNavMapAdd2(t *testing.T) {
- nM := NewNavigableMap(nil)
- path := []string{"FistLever2", "SecondLevel2", "Field2"}
- data := 123
- nM.Set(path, data, false, true)
- path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}
- data1 := 123.123
- nM.Set(path, data1, false, true)
- path = []string{"FistLever2", "Field3"}
- data2 := "Value3"
- nM.Set(path, data2, false, true)
- path = []string{"Field4"}
- data3 := &testStruct{
- Item1: "Ten",
- Item2: 10,
- }
- nM.Set(path, data3, false, true)
- eNavMap := NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": 123.123,
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": 123,
- },
- "Field3": "Value3",
- },
- "Field4": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- },
- }
- if !reflect.DeepEqual(nM.data, eNavMap.data) {
- t.Errorf("Expecting: %+v, received: %+v", eNavMap.data, nM.data)
- }
-}
-
-func TestNavMapSetWithAppend(t *testing.T) {
- nM := NewNavigableMap(nil)
- itm := &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- }
- nM.Set(itm.Path, []*NMItem{itm}, true, true)
- itm = &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- }
- nM.Set(itm.Path, []*NMItem{itm}, true, true)
- eNavMap := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- },
- },
- },
- },
- },
- },
- order: [][]string{
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- },
- }
- if !reflect.DeepEqual(nM, eNavMap) {
- t.Errorf("expecting: %+v, received: %+v", eNavMap, nM)
- }
- nM = NewNavigableMap(nil)
- itm = &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- }
- nM.Set(itm.Path, []*NMItem{itm}, false, true)
- itm = &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- }
- nM.Set(itm.Path, []*NMItem{itm}, false, true)
- eNavMap = &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- },
- },
- },
- },
- },
- },
- order: [][]string{
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- },
- }
- if !reflect.DeepEqual(nM, eNavMap) {
- t.Errorf("expecting: %+v, received: %+v", eNavMap, nM)
- }
-}
-
-func TestNavMapItems(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": "Value2",
- },
- "Field3": "Value3",
- },
- "Field4": "Val4",
- }
- nM := NewNavigableMap(myData)
- eItems := []*NMItem{
- &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: "Value2",
- },
- &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- &NMItem{
- Path: []string{"Field4"},
- Data: "Val4",
- },
- }
- if vals := nM.Values(); !reflect.DeepEqual(len(vals), len(eItems)) {
- t.Errorf("Expecting: %+v, received: %+v",
- utils.ToJSON(eItems), utils.ToJSON(vals))
- }
-}
-
-func TestNavMapItems2(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": 123.123,
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": 123,
- },
- "Field3": "Value3",
- },
- "Field4": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- }
- nM := NewNavigableMap(myData)
- eItems := []*NMItem{
- &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: 123.123,
- },
- &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: 123,
- },
- &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- &NMItem{
- Path: []string{"Field4"},
- Data: &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- },
- }
- if vals := nM.Values(); !reflect.DeepEqual(len(vals), len(eItems)) {
- t.Errorf("Expecting: %+v, received: %+v",
- utils.ToJSON(eItems), utils.ToJSON(vals))
- }
-}
-
-func TestNavMapOrder(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: "Value2",
- },
- },
- "Field3": &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- },
- "Field4": &NMItem{
- Path: []string{"Field4"},
- Data: "Val4",
- },
- }
- order := [][]string{
- []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- []string{"FistLever2", "SecondLevel2", "Field2"},
- []string{"FistLever2", "Field3"},
- []string{"Field4"},
- }
- nM := NewNavigableMap(myData)
- nM.order = order
- eItems := []interface{}{
- &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: "Value2",
- },
- &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- &NMItem{
- Path: []string{"Field4"},
- Data: "Val4",
- },
- }
- if vals := nM.Values(); !reflect.DeepEqual(vals, eItems) {
- t.Errorf("Expecting: %+v, received: %+v",
- utils.ToJSON(eItems), utils.ToJSON(vals))
- }
-}
-
-func TestNavMapOrder2(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: "Value2",
- },
- },
- "Field3": &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- },
- "Field4": &NMItem{
- Path: []string{"Field4"},
- Data: "Val4",
- },
- }
- order := [][]string{
- []string{"FistLever2", "SecondLevel2", "Field2"},
- []string{"Field4"},
- []string{"FistLever2", "Field3"},
- []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- }
- nM := NewNavigableMap(myData)
- nM.order = order
- eItems := []interface{}{
- &NMItem{
- Path: []string{"FistLever2", "SecondLevel2", "Field2"},
- Data: "Value2",
- },
- &NMItem{
- Path: []string{"Field4"},
- Data: "Val4",
- },
- &NMItem{
- Path: []string{"FistLever2", "Field3"},
- Data: "Value3",
- },
- &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- }
- if vals := nM.Values(); !reflect.DeepEqual(eItems, vals) {
- t.Errorf("Expecting: %+v, received: %+v",
- utils.ToJSON(eItems), utils.ToJSON(vals))
- }
-}
-
-func TestNavMapIndexMapElementes(t *testing.T) {
- var elmsOut []interface{}
- ifaceMap := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": "Value2",
- },
- "Field3": "Value3",
- },
- "Field4": "Val4",
- }
- eItems := []interface{}{"Val1", "Value2", "Value3", "Val4"}
- indexMapElements(ifaceMap, []string{}, &elmsOut)
- if !reflect.DeepEqual(len(elmsOut), len(eItems)) {
- t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eItems), utils.ToJSON(elmsOut))
- }
-}
-
-func TestNavMapString(t *testing.T) {
- myData := map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": "Value2",
- },
- "Field3": "Value3",
- },
- "Field4": "Val4",
- }
- nM := NewNavigableMap(myData)
- eStr := utils.ToJSON(myData)
- if !reflect.DeepEqual(nM.String(), eStr) {
- t.Errorf("Expecting: %+v, received: %+v", eStr, nM.String())
- }
-}
-
-func TestNavMapAsXMLElements(t *testing.T) {
- nM := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- &NMItem{Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1"}},
- },
- },
- },
- "FirstLevel2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "SecondLevel2", "Field2"},
- Data: "attrVal1",
- Config: &FCTemplate{Tag: "AttributeTest", AttributeID: "attribute1"}},
- &NMItem{Path: []string{"FirstLevel2", "SecondLevel2", "Field2"},
- Data: "Value2"}},
- },
- "Field3": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field3"},
- Data: "Value3"}},
- "Field5": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field5"},
- Data: "Value5"},
- &NMItem{Path: []string{"FirstLevel2", "Field5"},
- Data: "attrVal5",
- Config: &FCTemplate{Tag: "AttributeTest", AttributeID: "attribute5"}}},
- "Field6": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field6"},
- Data: "Value6",
- Config: &FCTemplate{Tag: "NewBranchTest", NewBranch: true}},
- &NMItem{Path: []string{"FirstLevel2", "Field6"},
- Data: "attrVal6",
- Config: &FCTemplate{Tag: "AttributeTest", AttributeID: "attribute6"}},
- },
- },
- "Field4": []*NMItem{
- &NMItem{Path: []string{"Field4"},
- Data: "Val4"},
- &NMItem{Path: []string{"Field4"},
- Data: "attrVal2",
- Config: &FCTemplate{Tag: "AttributeTest", AttributeID: "attribute2"}}},
- },
- order: [][]string{
- []string{"FirstLevel2", "SecondLevel2", "Field2"},
- []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- []string{"FirstLevel2", "Field3"},
- []string{"FirstLevel2", "Field5"},
- []string{"Field4"},
- []string{"FirstLevel2", "Field6"},
- },
- }
- eXMLElmnts := []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[0][0]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[0][1]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[0][2]},
- Attributes: []*xml.Attr{
- &xml.Attr{
- Name: xml.Name{Local: "attribute1"},
- Value: "attrVal1",
- },
- },
- Value: "Value2",
- },
- },
- },
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[2][1]},
- Value: "Value3",
- },
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[3][1]},
- Attributes: []*xml.Attr{
- &xml.Attr{
- Name: xml.Name{Local: "attribute5"},
- Value: "attrVal5",
- },
- },
- Value: "Value5",
- },
- },
- },
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[1][0]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[1][1]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[1][2]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[1][3]},
- Value: "Val1",
- },
- },
- },
- },
- },
- },
- },
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[4][0]},
- Attributes: []*xml.Attr{
- &xml.Attr{
- Name: xml.Name{Local: "attribute2"},
- Value: "attrVal2",
- },
- },
- Value: "Val4",
- },
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[5][0]},
- Elements: []*XMLElement{
- &XMLElement{
- XMLName: xml.Name{Local: nM.order[5][1]},
- Attributes: []*xml.Attr{
- &xml.Attr{
- Name: xml.Name{Local: "attribute6"},
- Value: "attrVal6",
- },
- },
- Value: "Value6",
- },
- },
- },
- }
- xmlEnts, err := nM.AsXMLElements()
- if err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eXMLElmnts, xmlEnts) {
- t.Errorf("expecting: %s, received: %s", utils.ToJSON(eXMLElmnts), utils.ToJSON(xmlEnts))
- }
- eXML := []byte(`
-
- Value2
-
- Value3
- Value5
-
-
-
-
- Val1
-
-
-
-Val4
-
- Value6
-`)
- if output, err := xml.MarshalIndent(xmlEnts, "", " "); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eXML, output) {
- t.Errorf("expecting: \n%s, received: \n%s\n", string(eXML), string(output))
- }
-}
-
-func TestIndexMapPaths(t *testing.T) {
- mp := make(map[string]interface{})
- parsedPaths := make([][]string, 0)
- eParsedPaths := make([][]string, 0)
- if indexMapPaths(mp, nil, &parsedPaths); !reflect.DeepEqual(eParsedPaths, parsedPaths) {
- t.Errorf("expecting: %+v, received: %+v", eParsedPaths, parsedPaths)
- }
- mp = map[string]interface{}{
- "a": "a",
- }
- parsedPaths = make([][]string, 0)
- eParsedPaths = [][]string{
- []string{"a"},
- }
- if indexMapPaths(mp, nil, &parsedPaths); !reflect.DeepEqual(eParsedPaths, parsedPaths) {
- t.Errorf("expecting: %+v, received: %+v", eParsedPaths, parsedPaths)
- }
- mp = map[string]interface{}{
- "a": map[string]interface{}{
- "a1": "a",
- },
- "b": "b",
- }
- parsedPaths = make([][]string, 0)
- eParsedPaths = [][]string{
- []string{"a", "a1"},
- []string{"b"},
- }
- if indexMapPaths(mp, nil, &parsedPaths); len(eParsedPaths) != len(parsedPaths) {
- t.Errorf("expecting: %+v, received: %+v", eParsedPaths, parsedPaths)
- }
-}
-
-func TestNavMapAsCGREvent(t *testing.T) {
- nM := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- &NMItem{Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1"}},
- },
- },
- },
- "FirstLevel2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "SecondLevel2", "Field2"},
- Data: "attrVal1",
- Config: &FCTemplate{Tag: "AttributeTest",
- AttributeID: "attribute1"}},
- &NMItem{Path: []string{"FirstLevel2", "SecondLevel2", "Field2"},
- Data: "Value2"}},
- },
- "Field3": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field3"},
- Data: "Value3"}},
- "Field5": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field5"},
- Data: "Value5"},
- &NMItem{Path: []string{"FirstLevel2", "Field5"},
- Data: "attrVal5",
- Config: &FCTemplate{Tag: "AttributeTest",
- AttributeID: "attribute5"}}},
- "Field6": []*NMItem{
- &NMItem{Path: []string{"FirstLevel2", "Field6"},
- Data: "Value6",
- Config: &FCTemplate{Tag: "NewBranchTest",
- NewBranch: true}},
- &NMItem{Path: []string{"FirstLevel2", "Field6"},
- Data: "attrVal6",
- Config: &FCTemplate{Tag: "AttributeTest",
- AttributeID: "attribute6"}},
- },
- },
- "Field4": []*NMItem{
- &NMItem{Path: []string{"Field4"},
- Data: "Val4"},
- &NMItem{Path: []string{"Field4"},
- Data: "attrVal2",
- Config: &FCTemplate{Tag: "AttributeTest",
- AttributeID: "attribute2"}}},
- },
- }
- eEv := map[string]interface{}{
- "FirstLevel2.SecondLevel2.Field2": "Value2",
- "FirstLevel.SecondLevel.ThirdLevel.Fld1": "Val1",
- "FirstLevel2.Field3": "Value3",
- "FirstLevel2.Field5": "Value5",
- "FirstLevel2.Field6": "Value6",
- "Field4": "Val4",
- }
- if cgrEv := nM.AsCGREvent("cgrates.org",
- utils.NestingSep); cgrEv.Tenant != "cgrates.org" ||
- cgrEv.Time == nil ||
- !reflect.DeepEqual(eEv, cgrEv.Event) {
- t.Errorf("expecting: %+v, \nreceived: %+v", utils.ToJSON(eEv), utils.ToJSON(cgrEv.Event))
- }
-}
-
-func TestNavMapMerge(t *testing.T) {
- nM2 := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- },
- },
- },
- },
- },
- },
- order: [][]string{
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- {"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- },
- }
- nM := NewNavigableMap(nil)
- nM.Merge(nM2)
- if !reflect.DeepEqual(nM2, nM) {
- 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 TestNavMapGetField(t *testing.T) {
- nM := &NavigableMap{
- data: map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": []*NMItem{
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- },
- {
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val2",
- },
- },
- },
- },
- },
- "FirstLevel2": map[string]interface{}{
- "SecondLevel2": []map[string]interface{}{
- map[string]interface{}{
- "ThirdLevel2": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- map[string]interface{}{
- "Count": 10,
- "ThirdLevel2": map[string]interface{}{
- "Fld2": []string{"Val1", "Val2", "Val3"},
- },
- },
- },
- },
- "AnotherFirstLevel": "ValAnotherFirstLevel",
- },
- }
- pth := []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1[0]"}
- eFld := &NMItem{
- Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"},
- Data: "Val1",
- }
- if fld, err := nM.GetField(pth); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eFld, fld) {
- t.Errorf("expecting: %s, received: %s", utils.ToIJSON(eFld), utils.ToIJSON(fld))
- }
- eFld2 := map[string]interface{}{"Fld1": "Val1"}
- pth = []string{"FirstLevel2", "SecondLevel2[0]", "ThirdLevel2"}
- if fld, err := nM.GetField(pth); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eFld2, fld) {
- t.Errorf("expecting: %s, received: %s", utils.ToIJSON(eFld2), utils.ToIJSON(fld))
- }
- eFld3 := "ValAnotherFirstLevel"
- pth = []string{"AnotherFirstLevel"}
- if fld, err := nM.GetField(pth); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eFld3, fld) {
- t.Errorf("expecting: %s, received: %s", utils.ToIJSON(eFld3), utils.ToIJSON(fld))
- }
- pth = []string{"AnotherFirstLevel2"}
- if _, err := nM.GetField(pth); err == nil || err != utils.ErrNotFound {
- t.Error(err)
- }
- pth = []string{"FirstLevel", "SecondLevel[1]", "ThirdLevel", "Fld1[0]"}
- if _, err := nM.GetField(pth); err == nil || err != utils.ErrNotFound {
- t.Error(err)
- }
-}
-
-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))
- }
-}
-
-func TestNavMapGetKeys(t *testing.T) {
- navMp := NewNavigableMap(
- map[string]interface{}{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": 123.123,
- },
- },
- },
- "FistLever2": map[string]interface{}{
- "SecondLevel2": map[string]interface{}{
- "Field2": 123,
- },
- "Field3": "Value3",
- "Field4": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- },
- "Field5": &testStruct{
- Item1: "Ten",
- Item2: 10,
- },
- "Field6": []string{"1", "2"},
- },
- )
- expKeys := []string{
- "FirstLevel",
- "FirstLevel.SecondLevel",
- "FirstLevel.SecondLevel.ThirdLevel",
- "FirstLevel.SecondLevel.ThirdLevel.Fld1",
- "FistLever2",
- "FistLever2.SecondLevel2",
- "FistLever2.SecondLevel2.Field2",
- "FistLever2.Field3",
- "FistLever2.Field4",
- "FistLever2.Field4.Item1",
- "FistLever2.Field4.Item2",
- "Field5",
- "Field5.Item1",
- "Field5.Item2",
- "Field6",
- "Field6[0]",
- "Field6[1]",
- }
- keys := navMp.GetKeys()
- sort.Strings(expKeys)
- sort.Strings(keys)
- if !reflect.DeepEqual(expKeys, keys) {
- t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(expKeys), utils.ToJSON(keys))
- }
-}
-
func TestNMAsXMLElements(t *testing.T) {
nM := utils.NewOrderedNavigableMap()
order := []utils.PathItems{
@@ -1569,12 +412,12 @@ func BenchmarkOrderdNavigableMapSet2(b *testing.B) {
}
}
-func BenchmarkNavigableMapOld1Set(b *testing.B) {
- nm := NewNavigableMap(nil)
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- for _, data := range gen {
- nm.Set(data.path, data.data, false, true)
- }
- }
-}
+// func BenchmarkNavigableMapOld1Set(b *testing.B) {
+// nm := NewNavigableMap(nil)
+// b.ResetTimer()
+// for n := 0; n < b.N; n++ {
+// for _, data := range gen {
+// nm.Set(data.path, data.data, false, true)
+// }
+// }
+// }
diff --git a/config/slicedp.go b/config/slicedp.go
index 7dac8430e..ee7a69567 100644
--- a/config/slicedp.go
+++ b/config/slicedp.go
@@ -28,14 +28,14 @@ import (
// NewSliceDP constructs a utils.DataProvider
func NewSliceDP(record []string) (dP utils.DataProvider) {
- dP = &SliceDP{req: record, cache: NewNavigableMap(nil)}
+ dP = &SliceDP{req: record, cache: utils.MapStorage{}}
return
}
// SliceDP implements engine.utils.DataProvider so we can pass it to filters
type SliceDP struct {
req []string
- cache *NavigableMap
+ cache utils.MapStorage
}
// String is part of engine.utils.DataProvider interface
@@ -65,7 +65,7 @@ func (cP *SliceDP) FieldAsInterface(fldPath []string) (data interface{}, err err
} else {
data = cP.req[cfgFieldIdx]
}
- cP.cache.Set(fldPath, data, false, false)
+ cP.cache.Set(fldPath, data)
return
}
diff --git a/config/xmldp.go b/config/xmldp.go
index 13bf8549a..2c48a7a6c 100644
--- a/config/xmldp.go
+++ b/config/xmldp.go
@@ -30,7 +30,7 @@ import (
// NewXmlProvider constructs a utils.DataProvider
func NewXmlProvider(req *xmlquery.Node, cdrPath utils.HierarchyPath) (dP utils.DataProvider) {
- dP = &XmlProvider{req: req, cdrPath: cdrPath, cache: NewNavigableMap(nil)}
+ dP = &XmlProvider{req: req, cdrPath: cdrPath, cache: utils.MapStorage{}}
return
}
@@ -38,7 +38,7 @@ func NewXmlProvider(req *xmlquery.Node, cdrPath utils.HierarchyPath) (dP utils.D
type XmlProvider struct {
req *xmlquery.Node
cdrPath utils.HierarchyPath //used to compute relative path
- cache *NavigableMap
+ cache utils.MapStorage
}
// String is part of engine.utils.DataProvider interface
@@ -77,7 +77,7 @@ func (xP *XmlProvider) FieldAsInterface(fldPath []string) (data interface{}, err
}
}
data, err = ElementText(xP.req, relPath.AsString("/", false))
- xP.cache.Set(fldPath, data, false, false)
+ xP.cache.Set(fldPath, data)
return
}
diff --git a/dispatchers/dispatchers.go b/dispatchers/dispatchers.go
index dc7d075c2..4ab28a931 100755
--- a/dispatchers/dispatchers.go
+++ b/dispatchers/dispatchers.go
@@ -138,8 +138,7 @@ func (dS *DispatcherService) dispatcherProfileForEvent(ev *utils.CGREvent,
return nil, err
}
}
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, ev.Event, false, false)
+ evNm := utils.MapStorage{utils.MetaReq: ev.Event}
for prflID := range prflIDs {
prfl, err := dS.dm.GetDispatcherProfile(ev.Tenant, prflID, true, true, utils.NonTransactional)
if err != nil {
diff --git a/ees/ees.go b/ees/ees.go
index 1ebcde7db..d8e31b868 100644
--- a/ees/ees.go
+++ b/ees/ees.go
@@ -140,9 +140,7 @@ func (eeS *EventExporterS) V1ProcessEvent(cgrEv *utils.CGREventWithOpts, rply *s
}
if len(eeCfg.Filters) != 0 {
- cgrDp := config.NewNavigableMap(map[string]interface{}{
- utils.MetaReq: cgrEv.Event,
- })
+ cgrDp := utils.MapStorage{utils.MetaReq: cgrEv.Event}
tnt := cgrEv.Tenant
if eeTnt, errTnt := eeCfg.Tenant.ParseEvent(cgrEv.Event); errTnt == nil && eeTnt != utils.EmptyString {
tnt = eeTnt
diff --git a/ees/filecsv.go b/ees/filecsv.go
index 39d9495f4..a550ce0b8 100644
--- a/ees/filecsv.go
+++ b/ees/filecsv.go
@@ -103,9 +103,7 @@ func (fCsv *FileCSVee) ExportEvent(cgrEv *utils.CGREvent) (err error) {
defer fCsv.Unlock()
fCsv.numberOfEvents++
var csvRecord []string
- navMp := config.NewNavigableMap(map[string]interface{}{
- utils.MetaReq: cgrEv.Event,
- })
+ navMp := utils.MapStorage{utils.MetaReq: cgrEv.Event}
for _, cfgFld := range fCsv.cgrCfg.EEsCfg().Exporters[fCsv.cfgIdx].ContentFields() {
if pass, err := fCsv.filterS.Pass(cgrEv.Tenant, cfgFld.Filters,
navMp); err != nil || !pass {
diff --git a/engine/action.go b/engine/action.go
index bf7b86b2a..3b8cc95e2 100644
--- a/engine/action.go
+++ b/engine/action.go
@@ -894,7 +894,7 @@ func (apl Actions) Clone() (interface{}, error) {
// newCdrLogProvider constructs a DataProvider
func newCdrLogProvider(acnt *Account, action *Action) (dP utils.DataProvider) {
- dP = &cdrLogProvider{acnt: acnt, action: action, cache: config.NewNavigableMap(nil)}
+ dP = &cdrLogProvider{acnt: acnt, action: action, cache: utils.MapStorage{}}
return
}
@@ -902,7 +902,7 @@ func newCdrLogProvider(acnt *Account, action *Action) (dP utils.DataProvider) {
type cdrLogProvider struct {
acnt *Account
action *Action
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -963,7 +963,7 @@ func (cdrP *cdrLogProvider) FieldAsInterface(fldPath []string) (data interface{}
default:
data = fldPath[0]
}
- cdrP.cache.Set(fldPath, data, false, false)
+ cdrP.cache.Set(fldPath, data)
return
}
diff --git a/engine/attributes.go b/engine/attributes.go
index 69d3e6db2..23e425853 100644
--- a/engine/attributes.go
+++ b/engine/attributes.go
@@ -95,10 +95,10 @@ func (alS *AttributeService) attributeProfileForEvent(args *AttrArgsProcessEvent
attrIDs = aPrflIDs.Slice()
}
- evNm := config.NewNavigableMap(map[string]interface{}{
+ evNm := utils.MapStorage{
utils.MetaReq: args.Event,
utils.MetaOpts: args.Opts,
- })
+ }
for _, apID := range attrIDs {
aPrfl, err := alS.dm.GetAttributeProfile(args.Tenant, apID, true, true, utils.NonTransactional)
if err != nil {
@@ -177,10 +177,10 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) (
Opts: args.Opts,
blocker: attrPrf.Blocker,
}
- evNm := config.NewNavigableMap(map[string]interface{}{
+ evNm := utils.MapStorage{
utils.MetaReq: rply.CGREvent.Event,
utils.MetaOpts: rply.Opts,
- })
+ }
for _, attribute := range attrPrf.Attributes {
//in case that we have filter for attribute send them to FilterS to be processed
if len(attribute.FilterIDs) != 0 {
@@ -337,7 +337,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) (
val, err = evNm.FieldAsString(strings.Split(attribute.Path, utils.NestingSep))
substitute = val + substitute
}
- evNm.Set(strings.Split(attribute.Path, utils.NestingSep), substitute, false, false)
+ evNm.Set(strings.Split(attribute.Path, utils.NestingSep), substitute)
}
return
}
diff --git a/engine/cdr.go b/engine/cdr.go
index 7505a583b..e232ade68 100644
--- a/engine/cdr.go
+++ b/engine/cdr.go
@@ -154,10 +154,10 @@ func (cdr *CDR) FormatCost(shiftDecimals, roundDecimals int) string {
// FieldAsString is used to retrieve fields as string, primary fields are const labeled
func (cdr *CDR) FieldAsString(rsrPrs *config.RSRParser) (parsed string, err error) {
parsed, err = rsrPrs.ParseDataProviderWithInterfaces(
- config.NewNavigableMap(map[string]interface{}{
+ utils.MapStorage{
utils.MetaReq: cdr.AsMapStringIface(),
utils.MetaEC: cdr.CostDetails,
- }), utils.NestingSep)
+ }, utils.NestingSep)
if err != nil {
return
}
@@ -167,10 +167,10 @@ func (cdr *CDR) FieldAsString(rsrPrs *config.RSRParser) (parsed string, err erro
// FieldsAsString concatenates values of multiple fields defined in template, used eg in CDR templates
func (cdr *CDR) FieldsAsString(rsrFlds config.RSRParsers) string {
outVal, err := rsrFlds.ParseDataProviderWithInterfaces(
- config.NewNavigableMap(map[string]interface{}{
+ utils.MapStorage{
utils.MetaReq: cdr.AsMapStringIface(),
utils.MetaEC: cdr.CostDetails,
- }), utils.NestingSep)
+ }, utils.NestingSep)
if err != nil {
return ""
}
@@ -403,10 +403,10 @@ func (cdr *CDR) formatField(cfgFld *config.FCTemplate, httpSkipTLSCheck bool,
// ExportRecord is a []string to keep it compatible with encoding/csv Writer
func (cdr *CDR) AsExportRecord(exportFields []*config.FCTemplate,
httpSkipTLSCheck bool, groupedCDRs []*CDR, filterS *FilterS) (expRecord []string, err error) {
- nM := config.NewNavigableMap(map[string]interface{}{
+ nM := utils.MapStorage{
utils.MetaReq: cdr.AsMapStringIface(),
utils.MetaEC: cdr.CostDetails,
- })
+ }
for _, cfgFld := range exportFields {
if !strings.HasPrefix(cfgFld.Path, utils.MetaExp+utils.NestingSep) {
continue
@@ -433,10 +433,10 @@ func (cdr *CDR) AsExportRecord(exportFields []*config.FCTemplate,
func (cdr *CDR) AsExportMap(exportFields []*config.FCTemplate, httpSkipTLSCheck bool,
groupedCDRs []*CDR, filterS *FilterS) (expMap map[string]string, err error) {
expMap = make(map[string]string)
- nM := config.NewNavigableMap(map[string]interface{}{
+ nM := utils.MapStorage{
utils.MetaReq: cdr.AsMapStringIface(),
utils.MetaEC: cdr.CostDetails,
- })
+ }
for _, cfgFld := range exportFields {
if !strings.HasPrefix(cfgFld.Path, utils.MetaExp+utils.NestingSep) {
continue
diff --git a/engine/cdre.go b/engine/cdre.go
index 66b42da4c..51e0159df 100644
--- a/engine/cdre.go
+++ b/engine/cdre.go
@@ -390,10 +390,10 @@ func (cdre *CDRExporter) processCDRs() (err error) {
continue
}
if len(cdre.exportTemplate.Filters) != 0 {
- cgrDp := config.NewNavigableMap(map[string]interface{}{
+ cgrDp := utils.MapStorage{
utils.MetaReq: cdr.AsMapStringIface(),
utils.MetaEC: cdr.CostDetails,
- })
+ }
if pass, err := cdre.filterS.Pass(cdre.exportTemplate.Tenant,
cdre.exportTemplate.Filters, cgrDp); err != nil || !pass {
continue // Not passes filters, ignore this CDR
diff --git a/engine/chargers.go b/engine/chargers.go
index 81d3e0c2a..0bc2af3be 100644
--- a/engine/chargers.go
+++ b/engine/chargers.go
@@ -68,8 +68,7 @@ func (cS *ChargerService) matchingChargerProfilesForEvent(cgrEv *utils.CGREvent)
return nil, err
}
matchingCPs := make(map[string]*ChargerProfile)
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, cgrEv.Event, false, false)
+ evNm := utils.MapStorage{utils.MetaReq: cgrEv.Event}
for cpID := range cpIDs {
cP, err := cS.dm.GetChargerProfile(cgrEv.Tenant, cpID, true, true, utils.NonTransactional)
if err != nil {
diff --git a/engine/eventcost.go b/engine/eventcost.go
index 1a9725762..3e840b9f5 100644
--- a/engine/eventcost.go
+++ b/engine/eventcost.go
@@ -24,7 +24,6 @@ import (
"net"
"time"
- "github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
@@ -37,7 +36,7 @@ func NewBareEventCost() *EventCost {
Rates: make(ChargedRates),
Timings: make(ChargedTimings),
Charges: make([]*ChargingInterval, 0),
- cache: config.NewNavigableMap(nil),
+ cache: utils.MapStorage{},
}
}
@@ -119,12 +118,12 @@ type EventCost struct {
Rates ChargedRates
Timings ChargedTimings
- cache *config.NavigableMap
+ cache utils.MapStorage
}
func (ec *EventCost) initCache() {
if ec != nil {
- ec.cache = config.NewNavigableMap(nil)
+ ec.cache = utils.MapStorage{}
}
}
@@ -867,9 +866,9 @@ func (ec *EventCost) FieldAsInterface(fldPath []string) (val interface{}, err er
}
val, err = ec.fieldAsInterface(fldPath)
if err == nil {
- ec.cache.Set(fldPath, val, false, false)
+ ec.cache.Set(fldPath, val)
} else if err == utils.ErrNotFound {
- ec.cache.Set(fldPath, nil, false, false)
+ ec.cache.Set(fldPath, nil)
}
return
}
diff --git a/engine/eventcost_test.go b/engine/eventcost_test.go
index c99563d13..f9f339e4c 100644
--- a/engine/eventcost_test.go
+++ b/engine/eventcost_test.go
@@ -204,7 +204,7 @@ var testEC = &EventCost{
StartTime: "00:00:00",
},
},
- cache: config.NewNavigableMap(nil),
+ cache: utils.MapStorage{},
}
func TestECClone(t *testing.T) {
@@ -2515,7 +2515,7 @@ func TestECSyncKeys(t *testing.T) {
StartTime: "00:00:00",
},
},
- cache: config.NewNavigableMap(nil),
+ cache: utils.MapStorage{},
}
ec.SyncKeys(refEC)
@@ -2552,7 +2552,7 @@ func TestECAsDataProvider(t *testing.T) {
func TestInitCache(t *testing.T) {
eventCost := &EventCost{}
eventCost.initCache()
- eOut := config.NewNavigableMap(nil)
+ eOut := utils.MapStorage{}
if !reflect.DeepEqual(eOut, eventCost.cache) {
t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eOut), utils.ToJSON(eventCost.cache))
}
@@ -2568,14 +2568,14 @@ func TestEventCostFieldAsInterface(t *testing.T) {
t.Errorf("Expecting: nil, received: %+v", rcv)
}
// item found in cache
- eventCost.cache = config.NewNavigableMap(map[string]interface{}{"test": nil})
+ eventCost.cache = utils.MapStorage{"test": nil}
if rcv, err := eventCost.FieldAsInterface([]string{"test"}); err == nil || err != utils.ErrNotFound {
t.Errorf("Expecting: nil, received: %+v", err)
} else if rcv != nil {
t.Errorf("Expecting: nil, received: %+v", rcv)
}
// data found in cache
- eventCost.cache = config.NewNavigableMap(map[string]interface{}{"test": "test"})
+ eventCost.cache = utils.MapStorage{"test": "test"}
if rcv, err := eventCost.FieldAsInterface([]string{"test"}); err != nil {
t.Errorf("Expecting: nil, received: %+v", err)
} else if rcv != "test" {
diff --git a/engine/filterhelpers.go b/engine/filterhelpers.go
index b355a8191..5efb8d1a4 100644
--- a/engine/filterhelpers.go
+++ b/engine/filterhelpers.go
@@ -33,18 +33,9 @@ func MatchingItemIDsForEvent(ev map[string]interface{}, stringFldIDs, prefixFldI
dm *DataManager, cacheID, itemIDPrefix string, indexedSelects, nestedFields bool) (itemIDs utils.StringMap, err error) {
itemIDs = make(utils.StringMap)
var allFieldIDs []string
- navEv := config.NewNavigableMap(ev)
+ navEv := utils.MapStorage(ev)
if indexedSelects && (stringFldIDs == nil || prefixFldIDs == nil) {
- if !nestedFields {
- allFieldIDs = make([]string, len(ev))
- i := 0
- for fldID := range ev {
- allFieldIDs[i] = fldID
- i++
- }
- } else {
- allFieldIDs = navEv.GetKeys()
- }
+ allFieldIDs = navEv.GetKeys(nestedFields)
}
// Guard will protect the function with automatic locking
lockID := utils.CacheInstanceToPrefix[cacheID] + itemIDPrefix
diff --git a/engine/filters.go b/engine/filters.go
index 5e5c56ae5..f4920f9ff 100644
--- a/engine/filters.go
+++ b/engine/filters.go
@@ -521,7 +521,7 @@ func newDynamicDP(cfg *config.CGRConfig, connMgr *ConnManager,
connMgr: connMgr,
tenant: tenant,
initialDP: initialDP,
- cache: config.NewNavigableMap(nil),
+ cache: utils.MapStorage{},
}
}
@@ -531,7 +531,7 @@ type dynamicDP struct {
tenant string
initialDP utils.DataProvider
- cache *config.NavigableMap
+ cache utils.MapStorage
}
func (dDP *dynamicDP) String() string { return utils.ToJSON(dDP) }
@@ -583,7 +583,7 @@ func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val interface{}, err e
}
//construct dataProvider from account and set it furthder
dp := config.NewObjectDP(account)
- dDP.cache.Set(fldPath[:2], dp, false, false)
+ dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaResources:
// sample of fieldName : ~*resources.ResourceID.Field
@@ -593,7 +593,7 @@ func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val interface{}, err e
return nil, err
}
dp := config.NewObjectDP(reply)
- dDP.cache.Set(fldPath[:2], dp, false, false)
+ dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaStats:
// sample of fieldName : ~*stats.StatID.*acd
@@ -605,7 +605,7 @@ func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val interface{}, err e
return nil, err
}
for k, v := range statValues {
- dDP.cache.Set([]string{utils.MetaStats, fldPath[1], k}, v, false, false)
+ dDP.cache.Set([]string{utils.MetaStats, fldPath[1], k}, v)
}
return dDP.cache.FieldAsInterface(fldPath)
default: // in case of constant we give an empty DataProvider ( empty navigable map )
diff --git a/engine/filters_test.go b/engine/filters_test.go
index 51498a094..ea7f5cc71 100644
--- a/engine/filters_test.go
+++ b/engine/filters_test.go
@@ -310,15 +310,15 @@ func TestFilterPassGreaterThan(t *testing.T) {
if err != nil {
t.Error(err)
}
- ev := config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 20, false, true)
+ ev := utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 20)
if passes, err := rf.passGreaterThan(ev); err != nil {
t.Error(err)
} else if !passes {
t.Error("not passing")
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 40, false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 40)
if passes, err := rf.passGreaterThan(ev); err != nil {
t.Error(err)
} else if passes {
@@ -351,8 +351,8 @@ func TestFilterPassGreaterThan(t *testing.T) {
} else if !passes {
t.Error("not passing")
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 20, false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 20)
if passes, err := rf.passGreaterThan(ev); err != nil {
t.Error(err)
} else if passes {
@@ -362,16 +362,16 @@ func TestFilterPassGreaterThan(t *testing.T) {
if err != nil {
t.Error(err)
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ACD"}, time.Duration(2*time.Minute), false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ACD"}, time.Duration(2*time.Minute))
if passes, err := rf.passGreaterThan(ev); err != nil {
t.Error(err)
} else if !passes {
t.Error("not pass")
}
// Second
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, time.Duration(20*time.Second), false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, time.Duration(20*time.Second))
rf, err = NewFilterRule("*gte", "~ASR", []string{"10s"})
if err != nil {
t.Error(err)
@@ -391,8 +391,8 @@ func TestFilterPassGreaterThan(t *testing.T) {
} else if !passes {
t.Error("passing")
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, float64(20*time.Second), false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, float64(20*time.Second))
rf, err = NewFilterRule("*gte", "~ASR", []string{"10s"})
if err != nil {
t.Error(err)
@@ -403,8 +403,8 @@ func TestFilterPassGreaterThan(t *testing.T) {
t.Error("passing")
}
//Here converter will be consider part of path and will get error : NOT_FOUND
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 20, false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 20)
rf, err = NewFilterRule("*gte", "~ASR{*duration_seconds}", []string{"10"})
if err != nil {
t.Error(err)
@@ -415,8 +415,8 @@ func TestFilterPassGreaterThan(t *testing.T) {
t.Error("passing")
}
//Here converter will be consider part of path and will get error : NOT_FOUND
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 20, false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 20)
rf, err = NewFilterRule("*gte", "~ASR{*duration_seconds}", []string{"10s"})
if err != nil {
t.Error(err)
@@ -433,15 +433,15 @@ func TestFilterpassEqualTo(t *testing.T) {
if err != nil {
t.Error(err)
}
- ev := config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 40.0, false, true)
+ ev := utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 40.0)
if passes, err := rf.passEqualTo(ev); err != nil {
t.Error(err)
} else if !passes {
t.Error("not passing")
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, 39, false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, 39)
if passes, err := rf.passEqualTo(ev); err != nil {
t.Error(err)
} else if passes {
@@ -456,8 +456,8 @@ func TestFilterpassEqualTo(t *testing.T) {
} else if !passes {
t.Error("not passing", passes)
}
- ev = config.NewNavigableMap(nil)
- ev.Set([]string{"ASR"}, "string1", false, true)
+ ev = utils.MapStorage{}
+ ev.Set([]string{"ASR"}, "string1")
rf, err = NewFilterRule(utils.MetaEqual, "~ASR", []string{"string1"})
if err != nil {
t.Error(err)
@@ -574,8 +574,8 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent := map[string]interface{}{
"Account": "1007",
}
- fEv := config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, failEvent, false, false)
+ fEv := utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, failEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*string:~*req.Account:1007:error"}, fEv); err != nil {
t.Error(err)
@@ -588,8 +588,8 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- pEv := config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ pEv := utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*string:~*req.Account:1007"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -609,10 +609,10 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
"Account": "1007",
}
- fEv = config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, failEvent, false, false)
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ fEv = utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, failEvent)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*prefix:~*req.Account:10"}, fEv); err != nil {
t.Errorf(err.Error())
@@ -650,10 +650,10 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
"Tenant": "cgrates.org",
}
- fEv = config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, failEvent, false, false)
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ fEv = utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, failEvent)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Tenant(~^cgr.*\\.org$)"}, fEv); err != nil {
t.Errorf(err.Error())
@@ -679,10 +679,10 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
utils.Weight: 20,
}
- fEv = config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, failEvent, false, false)
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ fEv = utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, failEvent)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*gte:~*req.Weight:20"}, fEv); err != nil {
t.Errorf(err.Error())
@@ -716,10 +716,10 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
"EmptyPtrSlice": &[]string{},
"EmptyPtrMap": &map[string]string{},
}
- fEv = config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, failEvent, false, false)
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ fEv = utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, failEvent)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent)
for key := range failEvent {
if pass, err := filterS.Pass("cgrates.org", []string{"*empty:~*req." + key + ":"},
fEv); err != nil {
@@ -768,10 +768,10 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
utils.Destination: "+4986517174963",
utils.Weight: 20,
}
- pEv1 := config.NewNavigableMap(nil)
- pEv1.Set([]string{utils.MetaReq}, passEvent1, false, false)
- pEv2 := config.NewNavigableMap(nil)
- pEv2.Set([]string{utils.MetaReq}, passEvent2, false, false)
+ pEv1 := utils.MapStorage{}
+ pEv1.Set([]string{utils.MetaReq}, passEvent1)
+ pEv2 := utils.MapStorage{}
+ pEv2.Set([]string{utils.MetaReq}, passEvent2)
if pass, err := filterS.Pass("cgrates.org",
[]string{}, pEv1); err != nil {
t.Errorf(err.Error())
@@ -787,8 +787,8 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
ev := map[string]interface{}{
"Test": "MultipleCharacter",
}
- pEv := config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, ev, false, false)
+ pEv := utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, ev)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Test(~^\\w{30,})"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -798,8 +798,8 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
ev = map[string]interface{}{
"Test": "MultipleCharacter123456789MoreThan30Character",
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, ev, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, ev)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Test(~^\\w{30,})"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -812,8 +812,8 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
"Test2": "MultipleCharacter",
},
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, ev, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, ev)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Test.Test2(~^\\w{30,})"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -825,8 +825,8 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
"Test2": "MultipleCharacter123456789MoreThan30Character",
},
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, ev, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, ev)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Test.Test2(~^\\w{30,})"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -841,8 +841,8 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
utils.SetupTime: time.Date(2017, 12, 1, 14, 25, 0, 0, time.UTC),
utils.Usage: "1m20s",
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, ev, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, ev)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*string:~*req.Account:1003", "*prefix:~*req.Destination:10",
"*suffix:~*req.Subject:03", "*rsr::~*req.Destination(1002)"},
@@ -865,8 +865,8 @@ func TestPassFilterMaxCost(t *testing.T) {
passEvent1 := map[string]interface{}{
"MaxUsage": time.Duration(-1),
}
- pEv := config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent1, false, false)
+ pEv := utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent1)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.MaxUsage{*duration_nanoseconds}(>0)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -877,8 +877,8 @@ func TestPassFilterMaxCost(t *testing.T) {
passEvent2 := map[string]interface{}{
"MaxUsage": time.Duration(0),
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent2, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent2)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.MaxUsage{*duration_nanoseconds}(>0)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -889,8 +889,8 @@ func TestPassFilterMaxCost(t *testing.T) {
passEvent3 := map[string]interface{}{
"MaxUsage": time.Duration(123),
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent3, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent3)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.MaxUsage{*duration_nanoseconds}(>0)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -917,8 +917,8 @@ func TestPassFilterMissingField(t *testing.T) {
passEvent1 := map[string]interface{}{
"test": "call",
}
- pEv := config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent1, false, false)
+ pEv := utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent1)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Category(^$)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -929,8 +929,8 @@ func TestPassFilterMissingField(t *testing.T) {
passEvent2 := map[string]interface{}{
"Category": "",
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent2, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent2)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Category(^$)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -941,8 +941,8 @@ func TestPassFilterMissingField(t *testing.T) {
passEvent3 := map[string]interface{}{
"Category": "call",
}
- pEv = config.NewNavigableMap(nil)
- pEv.Set([]string{utils.MetaReq}, passEvent3, false, false)
+ pEv = utils.MapStorage{}
+ pEv.Set([]string{utils.MetaReq}, passEvent3)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*rsr::~*req.Category(^$)"}, pEv); err != nil {
t.Errorf(err.Error())
@@ -1086,7 +1086,7 @@ func TestEventCostFilter(t *testing.T) {
},
}
cd.initCache()
- cgrDp := config.NewNavigableMap(map[string]interface{}{utils.MetaEC: cd})
+ cgrDp := utils.MapStorage{utils.MetaEC: cd}
if pass, err := filterS.Pass("cgrates.org",
[]string{"*string:~*ec.Charges[0].Increments[0].Accounting.Balance.Value:50"}, cgrDp); err != nil {
@@ -1183,8 +1183,8 @@ func TestPassPartial(t *testing.T) {
passEvent := map[string]interface{}{
"Account": "1007",
}
- fEv := config.NewNavigableMap(nil)
- fEv.Set([]string{utils.MetaReq}, passEvent, false, false)
+ fEv := utils.MapStorage{}
+ fEv.Set([]string{utils.MetaReq}, passEvent)
prefixes := []string{utils.DynamicDataPrefix + utils.MetaReq}
if pass, ruleList, err := filterS.LazyPass("cgrates.org",
[]string{"*string:~*req.Account:1007"}, fEv, prefixes); err != nil {
diff --git a/engine/libstats.go b/engine/libstats.go
index d1c89db00..2fd17dd5f 100644
--- a/engine/libstats.go
+++ b/engine/libstats.go
@@ -247,8 +247,7 @@ func (sq *StatQueue) addStatEvent(ev *utils.CGREvent, filterS *FilterS) (err err
ExpiryTime *time.Time
}{ev.ID, expTime})
var pass bool
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, ev.Event, false, false)
+ evNm := utils.MapStorage{utils.MetaReq: ev.Event}
for metricID, metric := range sq.SQMetrics {
if pass, err = filterS.Pass(ev.Tenant, metric.GetFilterIDs(),
evNm); err != nil {
diff --git a/engine/resources.go b/engine/resources.go
index cce73e262..c7650da8c 100644
--- a/engine/resources.go
+++ b/engine/resources.go
@@ -485,8 +485,7 @@ func (rS *ResourceService) matchingResourcesForEvent(ev *utils.CGREvent,
return
}
}
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, ev.Event, false, false)
+ evNm := utils.MapStorage{utils.MetaReq: ev.Event}
lockIDs := utils.PrefixSliceItems(rs.IDs(), utils.ResourcesPrefix)
guardian.Guardian.Guard(func() (gIface interface{}, gErr error) {
for resName := range rIDs {
diff --git a/engine/routes.go b/engine/routes.go
index dee11d50e..4ed8cc9e4 100644
--- a/engine/routes.go
+++ b/engine/routes.go
@@ -167,7 +167,7 @@ func (rpS *RouteService) matchingRouteProfilesForEvent(ev *utils.CGREvent, singl
if singleResult {
matchingRPrf = make([]*RouteProfile, 1)
}
- evNm := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: ev.Event})
+ evNm := utils.MapStorage{utils.MetaReq: ev.Event}
for lpID := range rPrfIDs {
rPrf, err := rpS.dm.GetRouteProfile(ev.Tenant, lpID, true, true, utils.NonTransactional)
if err != nil {
@@ -461,9 +461,10 @@ func (rpS *RouteService) populateSortingData(ev *utils.CGREvent, route *Route,
//filter the supplier
if len(route.lazyCheckRules) != 0 {
//construct the DP and pass it to filterS
- nM := config.NewNavigableMap(nil)
- nM.Set([]string{utils.MetaReq}, ev.Event, false, false)
- nM.Set([]string{utils.MetaVars}, sortedSpl.SortingData, false, false)
+ nM := utils.MapStorage{
+ utils.MetaReq: ev.Event,
+ utils.MetaVars: sortedSpl.SortingData,
+ }
for _, rule := range route.lazyCheckRules { // verify the rules remaining from PartialPass
if pass, err = rule.Pass(newDynamicDP(rpS.cgrcfg, rpS.connMgr, ev.Tenant, nM)); err != nil {
@@ -495,8 +496,7 @@ func (rpS *RouteService) sortedRoutesForEvent(args *ArgsGetRoutes) (sortedRoutes
extraOpts.sortingStragety = rPrfl.Sorting // populate sortingStrategy in extraOpts
//construct the DP and pass it to filterS
- nM := config.NewNavigableMap(nil)
- nM.Set([]string{utils.MetaReq}, args.CGREvent.Event, false, false)
+ nM := utils.MapStorage{utils.MetaReq: args.CGREvent.Event}
routeNew := make([]*Route, 0)
// apply filters for event
diff --git a/engine/stats.go b/engine/stats.go
index b4d855f56..a57f9ade1 100644
--- a/engine/stats.go
+++ b/engine/stats.go
@@ -168,8 +168,7 @@ func (sS *StatService) matchingStatQueuesForEvent(args *StatsArgsProcessEvent) (
}
sqIDs = mapIDs.Slice()
}
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, args.Event, false, false)
+ evNm := utils.MapStorage{utils.MetaReq: args.Event}
for _, sqID := range sqIDs {
sqPrfl, err := sS.dm.GetStatQueueProfile(args.Tenant, sqID, true, true, utils.NonTransactional)
if err != nil {
diff --git a/engine/thresholds.go b/engine/thresholds.go
index 2dd8fecab..c88e136dc 100644
--- a/engine/thresholds.go
+++ b/engine/thresholds.go
@@ -252,8 +252,9 @@ func (tS *ThresholdService) matchingThresholdsForEvent(args *ArgsProcessEvent) (
}
tIDs = tIDsMap.Slice()
}
- evNm := config.NewNavigableMap(nil)
- evNm.Set([]string{utils.MetaReq}, args.Event, false, false)
+ evNm := utils.MapStorage{
+ utils.MetaReq: args.Event,
+ }
for _, tID := range tIDs {
tPrfl, err := tS.dm.GetThresholdProfile(args.Tenant, tID, true, true, utils.NonTransactional)
if err != nil {
diff --git a/ers/filejson.go b/ers/filejson.go
index 2afacbf17..ea6b8d988 100644
--- a/ers/filejson.go
+++ b/ers/filejson.go
@@ -143,7 +143,7 @@ func (rdr *JSONFileER) processFile(fPath, fName string) (err error) {
reqVars := utils.NavigableMap2{utils.FileName: utils.NewNMData(fName)}
agReq := agents.NewAgentRequest(
- config.NewNavigableMap(data), reqVars,
+ utils.MapStorage(data), reqVars,
nil, nil, nil, rdr.Config().Tenant,
rdr.cgrCfg.GeneralCfg().DefaultTenant,
utils.FirstNonEmpty(rdr.Config().Timezone,
diff --git a/ers/kafka.go b/ers/kafka.go
index 7709f6802..1944c6b3b 100644
--- a/ers/kafka.go
+++ b/ers/kafka.go
@@ -159,7 +159,7 @@ func (rdr *KafkaER) processMessage(msg []byte) (err error) {
}
agReq := agents.NewAgentRequest(
- config.NewNavigableMap(decodedMessage), nil,
+ utils.MapStorage(decodedMessage), nil,
nil, nil, nil, rdr.Config().Tenant,
rdr.cgrCfg.GeneralCfg().DefaultTenant,
utils.FirstNonEmpty(rdr.Config().Timezone,
diff --git a/ers/sql.go b/ers/sql.go
index e47d11744..acda75429 100644
--- a/ers/sql.go
+++ b/ers/sql.go
@@ -186,7 +186,7 @@ func (rdr *SQLEventReader) readLoop(db *gorm.DB) {
func (rdr *SQLEventReader) processMessage(msg map[string]interface{}) (err error) {
agReq := agents.NewAgentRequest(
- config.NewNavigableMap(msg), nil,
+ utils.MapStorage(msg), nil,
nil, nil, nil, rdr.Config().Tenant,
rdr.cgrCfg.GeneralCfg().DefaultTenant,
utils.FirstNonEmpty(rdr.Config().Timezone,
diff --git a/general_tests/filters_test.go b/general_tests/filters_test.go
index af62a641c..febdd4793 100644
--- a/general_tests/filters_test.go
+++ b/general_tests/filters_test.go
@@ -105,8 +105,8 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent := map[string]interface{}{
utils.Destination: "+4986517174963",
}
- fEv := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: failEvent})
- pEv := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: passEvent})
+ fEv := utils.MapStorage{utils.MetaReq: failEvent}
+ pEv := utils.MapStorage{utils.MetaReq: passEvent}
if pass, err := filterS.Pass("cgrates.org",
[]string{"*destinations:~*req.Destination:EU"}, fEv); err != nil {
t.Errorf(err.Error())
diff --git a/loaders/libloader.go b/loaders/libloader.go
index 1f06cd03b..645b4ab62 100644
--- a/loaders/libloader.go
+++ b/loaders/libloader.go
@@ -85,7 +85,7 @@ func (ld LoaderData) UpdateFromCSV(fileName string, record []string,
// newCsvProvider constructs a DataProvider
func newCsvProvider(record []string, fileName string) (dP utils.DataProvider) {
- dP = &csvProvider{req: record, fileName: fileName, cache: config.NewNavigableMap(nil)}
+ dP = &csvProvider{req: record, fileName: fileName, cache: utils.MapStorage{}}
return
}
@@ -93,7 +93,7 @@ func newCsvProvider(record []string, fileName string) (dP utils.DataProvider) {
type csvProvider struct {
req []string
fileName string
- cache *config.NavigableMap
+ cache utils.MapStorage
}
// String is part of utils.DataProvider interface
@@ -116,7 +116,7 @@ func (cP *csvProvider) FieldAsInterface(fldPath []string) (data interface{}, err
idx = fldPath[1]
}
if fileName != "" && cP.fileName != fileName {
- cP.cache.Set(fldPath, nil, false, false)
+ cP.cache.Set(fldPath, nil)
return
}
if cfgFieldIdx, err := strconv.Atoi(idx); err != nil || len(cP.req) <= cfgFieldIdx {
@@ -125,7 +125,7 @@ func (cP *csvProvider) FieldAsInterface(fldPath []string) (data interface{}, err
data = cP.req[cfgFieldIdx]
}
- cP.cache.Set(fldPath, data, false, false)
+ cP.cache.Set(fldPath, data)
return
}
diff --git a/sessions/sessions.go b/sessions/sessions.go
index fbb4ae215..dd9d96d1c 100644
--- a/sessions/sessions.go
+++ b/sessions/sessions.go
@@ -988,7 +988,7 @@ func (sS *SessionS) filterSessions(sf *utils.SessionFilter, psv bool) (aSs []*Ex
return
}
var err error
- ev := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: me.Data()})
+ ev := utils.MapStorage{utils.MetaReq: me.Data()}
for _, fltr := range filterRules {
// we don't know how many values we have so we need to build the fieldValues DataProvider
if pass, err = fltr.Pass(ev); err != nil || !pass {
@@ -1044,7 +1044,7 @@ func (sS *SessionS) filterSessionsCount(sf *utils.SessionFilter, psv bool) (coun
return
}
var err error
- ev := config.NewNavigableMap(map[string]interface{}{utils.MetaReq: me.Data()})
+ ev := utils.MapStorage{utils.MetaReq: me.Data()}
for _, fltr := range filterRules {
// we don't know how many values we have so we need to build the fieldValues DataProvider
if pass, err = fltr.Pass(ev); err != nil || !pass {
diff --git a/utils/mapstorage.go b/utils/mapstorage.go
new file mode 100644
index 000000000..4819cca3d
--- /dev/null
+++ b/utils/mapstorage.go
@@ -0,0 +1,340 @@
+/*
+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 utils
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "reflect"
+ "strings"
+ "time"
+)
+
+// dataStorage is the DataProvider that can be updated
+type dataStorage interface {
+ DataProvider
+
+ Set(fldPath []string, val interface{}) error
+ Remove(fldPath []string) error
+ GetKeys(nesteed bool) []string
+}
+
+// MapStorage is the basic dataStorage
+type MapStorage map[string]interface{}
+
+// String returns the map as json string
+func (ms MapStorage) String() string { return ToJSON(ms) }
+
+// FieldAsInterface returns the value from the path
+func (ms MapStorage) FieldAsInterface(fldPath []string) (val interface{}, err error) {
+ if len(fldPath) == 0 {
+ err = errors.New("empty field path")
+ return
+
+ }
+ opath, indx := GetPathIndex(fldPath[0])
+ var has bool
+ if val, has = ms[opath]; !has {
+ err = ErrNotFound
+ return
+ }
+ if len(fldPath) == 1 {
+ if indx == nil {
+ return
+
+ }
+ switch rv := val.(type) {
+ case []string:
+ if len(rv) <= *indx {
+ return nil, ErrNotFound
+ }
+ val = rv[*indx]
+ return
+ case []interface{}:
+ if len(rv) <= *indx {
+ return nil, ErrNotFound
+ }
+ val = rv[*indx]
+ return
+ 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, ErrNotFound
+
+ }
+ if *indx >= vr.Len() {
+ return nil, ErrNotFound
+ }
+ return vr.Index(*indx).Interface(), nil
+
+ }
+ if indx == nil {
+ switch dp := ms[fldPath[0]].(type) {
+ case DataProvider:
+ return dp.FieldAsInterface(fldPath[1:])
+ case map[string]interface{}:
+ return MapStorage(dp).FieldAsInterface(fldPath[1:])
+ default:
+ err = ErrWrongPath
+
+ return
+ }
+ }
+ switch dp := ms[opath].(type) {
+ case []DataProvider:
+ if len(dp) <= *indx {
+ return nil, ErrNotFound
+ }
+ return dp[*indx].FieldAsInterface(fldPath[1:])
+ case []MapStorage:
+ if len(dp) <= *indx {
+ return nil, ErrNotFound
+ }
+ return dp[*indx].FieldAsInterface(fldPath[1:])
+ case []map[string]interface{}:
+ if len(dp) <= *indx {
+ return nil, ErrNotFound
+
+ }
+ return MapStorage(dp[*indx]).FieldAsInterface(fldPath[1:])
+ case []interface{}:
+ if len(dp) <= *indx {
+ return nil, ErrNotFound
+ }
+ switch ds := dp[*indx].(type) {
+ case DataProvider:
+ return ds.FieldAsInterface(fldPath[1:])
+ case map[string]interface{}:
+ return MapStorage(ds).FieldAsInterface(fldPath[1:])
+ default:
+ }
+ default:
+
+ }
+ err = ErrNotFound // xml compatible
+ val = nil
+ return
+}
+
+// FieldAsString returns the value from path as string
+func (ms MapStorage) FieldAsString(fldPath []string) (str string, err error) {
+ var val interface{}
+ if val, err = ms.FieldAsInterface(fldPath); err != nil {
+ return
+ }
+ return IfaceAsString(val), nil
+}
+
+// Set sets the value at the given path
+func (ms MapStorage) Set(fldPath []string, val interface{}) (err error) {
+ if len(fldPath) == 0 {
+ return ErrWrongPath
+ }
+ if len(fldPath) == 1 {
+ ms[fldPath[0]] = val
+
+ return
+ }
+
+ if _, has := ms[fldPath[0]]; !has {
+ nMap := MapStorage{}
+ ms[fldPath[0]] = nMap
+ return nMap.Set(fldPath[1:], val)
+ }
+ switch dp := ms[fldPath[0]].(type) {
+ case dataStorage:
+ return dp.Set(fldPath[1:], val)
+ case map[string]interface{}:
+ return MapStorage(dp).Set(fldPath[1:], val)
+ default:
+ return ErrWrongPath
+ }
+
+}
+
+// GetKeys returns all the keys from map
+func (ms MapStorage) GetKeys(nesteed bool) (keys []string) {
+ if !nesteed {
+ keys = make([]string, len(ms))
+ i := 0
+ for k := range ms {
+ keys[i] = k
+ i++
+
+ }
+ return
+ }
+ for k, v := range ms {
+ keys = append(keys, k)
+ switch rv := v.(type) {
+ case dataStorage:
+ for _, dsKey := range rv.GetKeys(nesteed) {
+ keys = append(keys, k+NestingSep+dsKey)
+ }
+ case map[string]interface{}:
+ for _, dsKey := range MapStorage(rv).GetKeys(nesteed) {
+ keys = append(keys, k+NestingSep+dsKey)
+ }
+ case []MapStorage:
+ for i, dp := range rv {
+ pref := k + fmt.Sprintf("[%v]", i)
+ keys = append(keys, pref)
+ for _, dsKey := range dp.GetKeys(nesteed) {
+ keys = append(keys, pref+NestingSep+dsKey)
+ }
+ }
+ case []dataStorage:
+ for i, dp := range rv {
+ pref := k + fmt.Sprintf("[%v]", i)
+ keys = append(keys, pref)
+ for _, dsKey := range dp.GetKeys(nesteed) {
+ keys = append(keys, pref+NestingSep+dsKey)
+ }
+ }
+ case []map[string]interface{}:
+ for i, dp := range rv {
+ pref := k + fmt.Sprintf("[%v]", i)
+ keys = append(keys, pref)
+ for _, dsKey := range MapStorage(dp).GetKeys(nesteed) {
+ keys = append(keys, pref+NestingSep+dsKey)
+ }
+ }
+ case []interface{}:
+ for i := range rv {
+ keys = append(keys, k+fmt.Sprintf("[%v]", i))
+ }
+ case []string:
+ for i := range rv {
+ keys = append(keys, k+fmt.Sprintf("[%v]", i))
+ }
+ default:
+ // ToDo:should not be called
+ keys = append(keys, getPathFromInterface(v, k+NestingSep)...)
+ }
+
+ }
+ return
+
+}
+
+// Remove removes the item at path
+func (ms MapStorage) Remove(fldPath []string) (err error) {
+ if len(fldPath) == 0 {
+ return ErrWrongPath
+ }
+ var val interface{}
+ var has bool
+ if val, has = ms[fldPath[0]]; !has {
+ return // ignore (already removed)
+ }
+ if len(fldPath) == 1 {
+ delete(ms, fldPath[0])
+
+ return
+ }
+ switch dp := val.(type) {
+ case dataStorage:
+ return dp.Remove(fldPath[1:])
+ case map[string]interface{}:
+ return MapStorage(dp).Remove(fldPath[1:])
+ default:
+ return ErrWrongPath
+
+ }
+
+}
+
+// RemoteHost is part of dataStorage interface
+func (ms MapStorage) RemoteHost() net.Addr {
+ return LocalAddr()
+}
+
+// ToDo: remove the following functions
+func getPathFromValue(in reflect.Value, prefix string) (out []string) {
+ switch in.Kind() {
+ case reflect.Ptr:
+ return getPathFromValue(in.Elem(), prefix)
+ case reflect.Array, reflect.Slice:
+ prefix = strings.TrimSuffix(prefix, NestingSep)
+ for i := 0; i < in.Len(); i++ {
+ pref := fmt.Sprintf("%s[%v]", prefix, i)
+ out = append(out, pref)
+ out = append(out, getPathFromValue(in.Index(i), pref+NestingSep)...)
+ }
+ case reflect.Map:
+ iter := reflect.ValueOf(in).MapRange()
+ for iter.Next() {
+ pref := prefix + iter.Key().String()
+ out = append(out, pref)
+ out = append(out, getPathFromValue(iter.Value(), pref+NestingSep)...)
+ }
+ case reflect.Struct:
+ inType := in.Type()
+ for i := 0; i < in.NumField(); i++ {
+ pref := prefix + inType.Field(i).Name
+ out = append(out, pref)
+ out = append(out, getPathFromValue(in.Field(i), pref+NestingSep)...)
+ }
+ case reflect.Invalid, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String, reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Interface:
+ default:
+ }
+ return
+}
+
+// used by MapStorage2 GetKeys to return all values
+func getPathFromInterface(in interface{}, prefix string) (out []string) {
+ switch vin := in.(type) {
+ case map[string]interface{}:
+ for k, val := range vin {
+ pref := prefix + k
+ out = append(out, pref)
+ out = append(out, getPathFromInterface(val, pref+NestingSep)...)
+ }
+ case []map[string]interface{}:
+ prefix = strings.TrimSuffix(prefix, NestingSep)
+ for i, val := range vin {
+ pref := fmt.Sprintf("%s[%v]", prefix, i)
+ out = append(out, pref)
+ out = append(out, getPathFromInterface(val, pref+NestingSep)...)
+ }
+ case []interface{}:
+ prefix = strings.TrimSuffix(prefix, NestingSep)
+ for i, val := range vin {
+ pref := fmt.Sprintf("%s[%v]", prefix, i)
+ out = append(out, pref)
+ out = append(out, getPathFromInterface(val, pref+NestingSep)...)
+ }
+ case []string:
+ prefix = strings.TrimSuffix(prefix, NestingSep)
+ for i := range vin {
+ pref := fmt.Sprintf("%s[%v]", prefix, i)
+ out = append(out, pref)
+ }
+ case nil, int, int32, int64, uint32, uint64, bool, float32, float64, []uint8, time.Duration, time.Time, string: //no path
+ default: //reflect based
+ out = getPathFromValue(reflect.ValueOf(vin), prefix)
+ }
+ return
+}
diff --git a/utils/mapstorage_test.go b/utils/mapstorage_test.go
new file mode 100644
index 000000000..a462f354f
--- /dev/null
+++ b/utils/mapstorage_test.go
@@ -0,0 +1,348 @@
+/*
+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 utils
+
+import (
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestNavMapGetFieldAsString(t *testing.T) {
+ nM := MapStorage{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": "Val1",
+ },
+ },
+ },
+ "AnotherFirstLevel": "ValAnotherFirstLevel",
+ }
+ eVal := "Val1"
+ if strVal, err := nM.FieldAsString(
+ strings.Split("FirstLevel>SecondLevel>ThirdLevel>Fld1", ">")); err != nil {
+ t.Error(err)
+ } else if strVal != eVal {
+ t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
+ }
+ eVal = "ValAnotherFirstLevel"
+ if strVal, err := nM.FieldAsString(
+ strings.Split("AnotherFirstLevel", ">")); err != nil {
+ t.Error(err)
+ } else if strVal != eVal {
+ t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
+ }
+ fPath := "NonExisting>AnotherFirstLevel"
+ if _, err := nM.FieldAsString(strings.Split(fPath, ">")); err.Error() !=
+ ErrNotFound.Error() {
+ t.Error(err)
+ }
+}
+
+type myEv map[string]interface{}
+
+func (ev myEv) AsMapStorage() (MapStorage, error) {
+ return MapStorage(ev), nil
+}
+
+func TestNavMapAsMapStorage(t *testing.T) {
+ myData := myEv{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": 123.123,
+ },
+ },
+ },
+ "FistLever2": map[string]interface{}{
+ "SecondLevel2": map[string]interface{}{
+ "Field2": 123,
+ },
+ "Field3": "Value3",
+ },
+ "Field4": &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ },
+ }
+
+ eNavMap := MapStorage{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": 123.123,
+ },
+ },
+ },
+ "FistLever2": map[string]interface{}{
+ "SecondLevel2": map[string]interface{}{
+ "Field2": 123,
+ },
+ "Field3": "Value3",
+ },
+ "Field4": &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ },
+ }
+
+ if rcv, err := myData.AsMapStorage(); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eNavMap, rcv) {
+ t.Errorf("Expecting: %+v, received: %+v", eNavMap, rcv)
+ }
+}
+
+type testStruct struct {
+ Item1 string
+ Item2 int
+}
+
+func TestNavMapAdd2(t *testing.T) {
+ nM := MapStorage{}
+ path := []string{"FistLever2", "SecondLevel2", "Field2"}
+ data := 123
+ nM.Set(path, data)
+ path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}
+ data1 := 123.123
+ nM.Set(path, data1)
+ path = []string{"FistLever2", "Field3"}
+ data2 := "Value3"
+ nM.Set(path, data2)
+ path = []string{"Field4"}
+ data3 := &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ }
+ nM.Set(path, data3)
+ eNavMap := MapStorage{
+ "FirstLevel": MapStorage{
+ "SecondLevel": MapStorage{
+ "ThirdLevel": MapStorage{
+ "Fld1": 123.123,
+ },
+ },
+ },
+ "FistLever2": MapStorage{
+ "SecondLevel2": MapStorage{
+ "Field2": 123,
+ },
+ "Field3": "Value3",
+ },
+ "Field4": &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ },
+ }
+ if !reflect.DeepEqual(nM, eNavMap) {
+ t.Errorf("Expecting: %+v, received: %+v", eNavMap, nM)
+ }
+}
+
+func TestNavMapString(t *testing.T) {
+ myData := map[string]interface{}{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": "Val1",
+ },
+ },
+ },
+ "FistLever2": map[string]interface{}{
+ "SecondLevel2": map[string]interface{}{
+ "Field2": "Value2",
+ },
+ "Field3": "Value3",
+ },
+ "Field4": "Val4",
+ }
+ nM := MapStorage(myData)
+ eStr := ToJSON(myData)
+ if !reflect.DeepEqual(nM.String(), eStr) {
+ t.Errorf("Expecting: %+v, received: %+v", eStr, nM.String())
+ }
+}
+
+func TestNavMapGetField(t *testing.T) {
+ nM := MapStorage{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": []interface{}{"Val1", "Val2"},
+ },
+ },
+ },
+ "FirstLevel2": map[string]interface{}{
+ "SecondLevel2": []map[string]interface{}{
+ map[string]interface{}{
+ "ThirdLevel2": map[string]interface{}{
+ "Fld1": "Val1",
+ },
+ },
+ map[string]interface{}{
+ "Count": 10,
+ "ThirdLevel2": map[string]interface{}{
+ "Fld2": []string{"Val1", "Val2", "Val3"},
+ },
+ },
+ },
+ },
+ "AnotherFirstLevel": "ValAnotherFirstLevel",
+ }
+ pth := []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1[0]"}
+ eFld := "Val1"
+ if fld, err := nM.FieldAsInterface(pth); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eFld, fld) {
+ t.Errorf("expecting: %s, received: %s", ToIJSON(eFld), ToIJSON(fld))
+ }
+ eFld2 := map[string]interface{}{"Fld1": "Val1"}
+ pth = []string{"FirstLevel2", "SecondLevel2[0]", "ThirdLevel2"}
+ if fld, err := nM.FieldAsInterface(pth); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eFld2, fld) {
+ t.Errorf("expecting: %s, received: %s", ToIJSON(eFld2), ToIJSON(fld))
+ }
+ eFld3 := "ValAnotherFirstLevel"
+ pth = []string{"AnotherFirstLevel"}
+ if fld, err := nM.FieldAsInterface(pth); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eFld3, fld) {
+ t.Errorf("expecting: %s, received: %s", ToIJSON(eFld3), ToIJSON(fld))
+ }
+ pth = []string{"AnotherFirstLevel2"}
+ if _, err := nM.FieldAsInterface(pth); err == nil || err != ErrNotFound {
+ t.Error(err)
+ }
+ pth = []string{"FirstLevel", "SecondLevel[1]", "ThirdLevel", "Fld1[0]"}
+ if _, err := nM.FieldAsInterface(pth); err == nil || err != ErrNotFound {
+ t.Error(err)
+ }
+}
+
+func TestNavMapFieldAsInterface(t *testing.T) {
+ nM := MapStorage{
+ "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 := 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 = ErrWrongPath
+ 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", ToJSON(eVal), 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", ToJSON(eVal), 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", ToJSON(eVal), ToJSON(rplyVal))
+ }
+}
+
+func TestNavMapGetKeys(t *testing.T) {
+ navMp := MapStorage{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "ThirdLevel": map[string]interface{}{
+ "Fld1": 123.123,
+ },
+ },
+ },
+ "FistLever2": map[string]interface{}{
+ "SecondLevel2": map[string]interface{}{
+ "Field2": 123,
+ },
+ "Field3": "Value3",
+ "Field4": &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ },
+ },
+ "Field5": &testStruct{
+ Item1: "Ten",
+ Item2: 10,
+ },
+ "Field6": []string{"1", "2"},
+ }
+ expKeys := []string{
+ "FirstLevel",
+ "FirstLevel.SecondLevel",
+ "FirstLevel.SecondLevel.ThirdLevel",
+ "FirstLevel.SecondLevel.ThirdLevel.Fld1",
+ "FistLever2",
+ "FistLever2.SecondLevel2",
+ "FistLever2.SecondLevel2.Field2",
+ "FistLever2.Field3",
+ "FistLever2.Field4",
+ "FistLever2.Field4.Item1",
+ "FistLever2.Field4.Item2",
+ "Field5",
+ "Field5.Item1",
+ "Field5.Item2",
+ "Field6",
+ "Field6[0]",
+ "Field6[1]",
+ }
+ keys := navMp.GetKeys(true)
+ sort.Strings(expKeys)
+ sort.Strings(keys)
+ if !reflect.DeepEqual(expKeys, keys) {
+ t.Errorf("Expecting: %+v, received: %+v", ToJSON(expKeys), ToJSON(keys))
+ }
+}