From 40c7699ee4b1857349772b4c5b5209fd84aca0e6 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 3 Jul 2018 17:01:35 +0200 Subject: [PATCH] NavigableMap should not analyze values internally to keep flexibility and speed --- agents/agentreq.go | 4 +- agents/agentreq_test.go | 30 +++---- agents/httpagent.go | 3 +- agents/libhttpagent.go | 2 +- agents/librad.go | 2 +- agents/librad_test.go | 2 +- config/cfgcdrfield.go | 10 ++- config/libconfig_json.go | 1 + engine/navigablemap.go | 141 +++++++++++++++++------------ engine/navigablemap_test.go | 175 +++++++++++++++++++++++------------- 10 files changed, 223 insertions(+), 147 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index 9b52aa8c4..1bc58f0dc 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -116,9 +116,7 @@ func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.CfgCdrField) ( if err != nil { return nil, err } - nM.Set( - &engine.NMItem{Path: strings.Split(tplFld.FieldId, - utils.HIERARCHY_SEP), Data: out}, true) + nM.Set(strings.Split(tplFld.FieldId, utils.HIERARCHY_SEP), out, true) } return } diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go index 37882581a..0cc8fb576 100644 --- a/agents/agentreq_test.go +++ b/agents/agentreq_test.go @@ -36,15 +36,15 @@ func TestAgReqAsNavigableMap(t *testing.T) { agReq := newAgentRequest(nil, nil, "cgrates.org", filterS) // populate request, emulating the way will be done in HTTPAgent - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.CGRID}, - Data: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String())}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.ToR}, Data: utils.VOICE}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.Account}, Data: "1001"}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.Destination}, Data: "1002"}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.AnswerTime}, - Data: time.Date(2013, 12, 30, 15, 0, 1, 0, time.UTC)}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.RequestType}, Data: utils.META_PREPAID}, false) - agReq.CGRRequest.Set(&engine.NMItem{Path: []string{utils.Usage}, Data: time.Duration(3 * time.Minute)}, false) + agReq.CGRRequest.Set([]string{utils.CGRID}, + utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), false) + agReq.CGRRequest.Set([]string{utils.ToR}, utils.VOICE, false) + agReq.CGRRequest.Set([]string{utils.Account}, "1001", false) + agReq.CGRRequest.Set([]string{utils.Destination}, "1002", false) + agReq.CGRRequest.Set([]string{utils.AnswerTime}, + time.Date(2013, 12, 30, 15, 0, 1, 0, time.UTC), false) + agReq.CGRRequest.Set([]string{utils.RequestType}, utils.META_PREPAID, false) + agReq.CGRRequest.Set([]string{utils.Usage}, time.Duration(3*time.Minute), false) cgrRply := map[string]interface{}{ utils.CapAttributes: map[string]interface{}{ @@ -99,12 +99,12 @@ func TestAgReqAsNavigableMap(t *testing.T) { "*cgrReply>Error", utils.INFIELD_SEP)}, } eMp := engine.NewNavigableMap(nil) - eMp.Set(&engine.NMItem{Path: []string{utils.Tenant}, Data: "cgrates.org"}, true) - eMp.Set(&engine.NMItem{Path: []string{utils.Account}, Data: "1001"}, true) - eMp.Set(&engine.NMItem{Path: []string{utils.Destination}, Data: "1002"}, true) - eMp.Set(&engine.NMItem{Path: []string{"RequestedUsage"}, Data: "180"}, true) - eMp.Set(&engine.NMItem{Path: []string{"PaypalAccount"}, Data: "cgrates@paypal.com"}, true) - eMp.Set(&engine.NMItem{Path: []string{"MaxUsage"}, Data: "120"}, true) + eMp.Set([]string{utils.Tenant}, "cgrates.org", true) + eMp.Set([]string{utils.Account}, "1001", true) + eMp.Set([]string{utils.Destination}, "1002", true) + eMp.Set([]string{"RequestedUsage"}, "180", true) + eMp.Set([]string{"PaypalAccount"}, "cgrates@paypal.com", true) + eMp.Set([]string{"MaxUsage"}, "120", true) if mpOut, err := agReq.AsNavigableMap(tplFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eMp, mpOut) { diff --git a/agents/httpagent.go b/agents/httpagent.go index 9f3e68f17..856c5639b 100644 --- a/agents/httpagent.go +++ b/agents/httpagent.go @@ -211,8 +211,7 @@ func (ha *HTTPAgent) processRequest(reqProcessor *config.HttpAgntProcCfg, var rplyCDRs string if err = ha.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rplyCDRs); err != nil { - agReq.CGRReply.Set( - &engine.NMItem{Path: []string{utils.Error}, Data: err.Error()}, false) + agReq.CGRReply.Set([]string{utils.Error}, err.Error(), false) } } if nM, err := agReq.AsNavigableMap(reqProcessor.ReplyFields); err != nil { diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go index 9969e53e7..0054f9987 100644 --- a/agents/libhttpagent.go +++ b/agents/libhttpagent.go @@ -67,7 +67,7 @@ func (hU *httpUrlDP) FieldAsInterface(fldPath []string) (data interface{}, err e } err = nil // cancel previous err data = hU.req.FormValue(fldPath[0]) - hU.cache.Set(&engine.NMItem{Path: fldPath, Data: data}, false) + hU.cache.Set(fldPath, data, false) return } diff --git a/agents/librad.go b/agents/librad.go index fec330555..0cf2f9d4e 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -411,6 +411,6 @@ func NewCGRReply(rply engine.NavigableMapper, if err != nil { return nil, err } - mp.Set(&engine.NMItem{Path: []string{utils.Error}, Data: ""}, false) // enforce empty error + mp.Set([]string{utils.Error}, "", false) // enforce empty error return mp, nil } diff --git a/agents/librad_test.go b/agents/librad_test.go index 5a7bbf2d0..89d0b7ed3 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -450,7 +450,7 @@ func TestNewCGRReply(t *testing.T) { }, } eCgrRply = engine.NewNavigableMap(ev) - eCgrRply.Set(&engine.NMItem{Path: []string{utils.Error}, Data: ""}, false) + eCgrRply.Set([]string{utils.Error}, "", false) if rpl, err := NewCGRReply(engine.NavigableMapper(ev), nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCgrRply, rpl) { diff --git a/config/cfgcdrfield.go b/config/cfgcdrfield.go index a8570b3fa..199d73fb7 100644 --- a/config/cfgcdrfield.go +++ b/config/cfgcdrfield.go @@ -34,6 +34,9 @@ func NewCfgCdrFieldFromCdrFieldJsonCfg(jsnCfgFld *CdrFieldJsonCfg) (*CfgCdrField if jsnCfgFld.Field_id != nil { cfgFld.FieldId = *jsnCfgFld.Field_id } + if jsnCfgFld.Attribute_id != nil { + cfgFld.AttributeID = *jsnCfgFld.Attribute_id + } if jsnCfgFld.Handler_id != nil { cfgFld.HandlerId = *jsnCfgFld.Handler_id } @@ -93,9 +96,10 @@ func NewCfgCdrFieldFromCdrFieldJsonCfg(jsnCfgFld *CdrFieldJsonCfg) (*CfgCdrField } type CfgCdrField struct { - Tag string // Identifier for the administrator - Type string // Type of field - FieldId string // Field identifier + Tag string // Identifier for the administrator + Type string // Type of field + FieldId string // Field identifier + AttributeID string Filters []string // list of filter profiles HandlerId string Value utils.RSRFields diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 2e1304b8f..3ac001ed9 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -145,6 +145,7 @@ type CdrFieldJsonCfg struct { Tag *string Type *string Field_id *string + Attribute_id *string Handler_id *string Value *string Append *bool diff --git a/engine/navigablemap.go b/engine/navigablemap.go index de1aaf45c..38fd8b4e8 100644 --- a/engine/navigablemap.go +++ b/engine/navigablemap.go @@ -22,7 +22,7 @@ import ( "encoding/xml" "errors" "fmt" - "strings" + "reflect" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -57,11 +57,11 @@ type NavigableMap struct { } // Add will add items into NavigableMap populating also order -func (nM *NavigableMap) Set(itm *NMItem, ordered bool) { +func (nM *NavigableMap) Set(path []string, data interface{}, ordered bool) { mp := nM.data - for i, spath := range itm.Path { - if i == len(itm.Path)-1 { // last path - mp[spath] = itm + for i, spath := range path { + if i == len(path)-1 { // last path + mp[spath] = data return } if _, has := mp[spath]; !has { @@ -70,7 +70,7 @@ func (nM *NavigableMap) Set(itm *NMItem, ordered bool) { mp = mp[spath].(map[string]interface{}) // so we can check further down } if ordered { - nM.order = append(nM.order, itm.Path) + nM.order = append(nM.order, path) } } @@ -85,30 +85,27 @@ func (nM *NavigableMap) FieldAsInterface(fldPath []string) (fldVal interface{}, var canCast bool for i, spath := range fldPath { if i == lenPath-1 { // lastElement - itmIface, has := lastMp[spath] + var has bool + fldVal, has = lastMp[spath] if !has { return nil, utils.ErrNotFound } - if itm, cast := itmIface.(*NMItem); cast { - return itm.Data, nil - } - return itmIface, nil - } else { - elmnt, has := lastMp[spath] - if !has { - err = fmt.Errorf("no map at path: <%s>", spath) + return + } + elmnt, has := lastMp[spath] + if !has { + err = fmt.Errorf("no map at path: <%s>", spath) + return + } + lastMp, canCast = elmnt.(map[string]interface{}) + if !canCast { + lastMpNM, canCast := elmnt.(*NavigableMap) // attempt to cast into NavigableMap + if !canCast { + err = fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}", + elmnt, elmnt, spath) return } - lastMp, canCast = elmnt.(map[string]interface{}) - if !canCast { - lastMpNM, canCast := elmnt.(*NavigableMap) // attempt to cast into NavigableMap - if !canCast { - err = fmt.Errorf("cannot cast field: <%+v> type: %T with path: <%s> to map[string]interface{}", - elmnt, elmnt, spath) - return - } - lastMp = lastMpNM.data - } + lastMp = lastMpNM.data } } err = errors.New("end of function") @@ -140,43 +137,38 @@ func (nM *NavigableMap) AsMapStringInterface() map[string]interface{} { } // indexMapElements will recursively go through map and index the element paths into elmns -func indexMapElements(mp map[string]interface{}, path []string, elms *[]*NMItem) { +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, elms) + indexMapElements(mpIface, vPath, vals) continue } - var elmsOut []*NMItem - if nMItem, isNMItem := v.(*NMItem); isNMItem { - elmsOut = append(*elms, nMItem) - } else { - elmsOut = append(*elms, &NMItem{Path: vPath, Data: v}) - } - - *elms = elmsOut + valsOut := append(*vals, v) + *vals = valsOut } } -// Items returns the items in map, ordered by order information -func (nM *NavigableMap) Items() (itms []*NMItem) { +// 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{}, &itms) + indexMapElements(nM.data, []string{}, &vals) return } - itms = make([]*NMItem, len(nM.order)) + vals = make([]interface{}, len(nM.order)) for i, path := range nM.order { val, _ := nM.FieldAsInterface(path) - itms[i] = &NMItem{Data: val, Path: path} + vals[i] = val } return } // AsNavigableMap implements both NavigableMapper as well as DataProvider interfaces -func (nM *NavigableMap) AsNavigableMap(tpl []*config.CfgCdrField) (oNM *NavigableMap, err error) { +func (nM *NavigableMap) AsNavigableMap( + tpl []*config.CfgCdrField) (oNM *NavigableMap, err error) { return nil, utils.ErrNotImplemented } @@ -193,32 +185,67 @@ func (nM *NavigableMap) Merge(nM2 *NavigableMap) { } } +// addNMItemToTokens will add a NMItem to tockens +// if itm.Config.IsAttribute the item data is added as Attribute to lastElmnt +// tokens and lastElmntIdx are overloaded on each function call +func addNMItemToTokens(tokens *[]xml.Token, lastElmntIdx *int, itm *NMItem, + lastPath []string) (err error) { + strVal, canCast := utils.CastFieldIfToString(itm.Data) + if !canCast { + return fmt.Errorf("cannot cast field: %s to string", utils.ToJSON(itm.Data)) + } + if *lastElmntIdx == -1 || len(itm.Path) <= 1 || + !reflect.DeepEqual(itm.Path[:len(itm.Path)-1], lastPath) { + charData := []byte("") + if itm.Config == nil || + itm.Config.AttributeID == "" { // not attribute but value + charData = []byte(strVal) + } + var elmLocal string + if len(itm.Path) != 0 { + elmLocal = itm.Path[len(itm.Path)-1] + } + t := xml.StartElement{Name: xml.Name{Local: elmLocal}} + *lastElmntIdx = len(*tokens) + tOut := append(*tokens, t, xml.CharData(charData), xml.EndElement{t.Name}) + *tokens = tOut + } + if itm.Config != nil && itm.Config.AttributeID != "" { + tkns := *tokens + lstElm := tkns[*lastElmntIdx].(xml.StartElement) + lstElm.Attr = append(lstElm.Attr, + xml.Attr{xml.Name{Local: itm.Config.AttributeID}, strVal}) + } + return +} + // MarshalXML implements xml.Marshaler func (nM *NavigableMap) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) { tokens := []xml.Token{start} - for _, itm := range nM.Items() { - strVal, canCast := utils.CastFieldIfToString(itm.Data) - if !canCast { - return fmt.Errorf("cannot cast field: %s to string", utils.ToJSON(itm.Data)) + var prevItem *NMItem + lastElmntIdx := utils.IntPointer(-1) + for _, itm := range nM.Values() { + nmItems, isNMItems := itm.([]*NMItem) + if !isNMItems { + return fmt.Errorf("map value: <%+v> not []*NMItem", nmItems) + } + var lastPath []string + if prevItem != nil { + lastPath = prevItem.Path + } + for _, itm := range nmItems { + if err = addNMItemToTokens(&tokens, lastElmntIdx, itm, lastPath); err != nil { + return + } } - t := xml.StartElement{Name: xml.Name{"", strings.Join(itm.Path, ">")}} - tokens = append(tokens, t, xml.CharData([]byte(strVal)), xml.EndElement{t.Name}) } - tokens = append(tokens, xml.EndElement{start.Name}) - + //fmt.Printf("## OUT: %s\n", utils.ToJSON(tokens)) for _, t := range tokens { - err := e.EncodeToken(t) - if err != nil { + if err = e.EncodeToken(t); err != nil { return err } } - // flush to ensure tokens are written return e.Flush() - if err != nil { - return err - } - - return nil } diff --git a/engine/navigablemap_test.go b/engine/navigablemap_test.go index 8850fc27d..26c63be01 100644 --- a/engine/navigablemap_test.go +++ b/engine/navigablemap_test.go @@ -18,11 +18,14 @@ along with this program. If not, see package engine import ( + "encoding/xml" "errors" + //"fmt" "reflect" "strings" "testing" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -148,36 +151,32 @@ func TestNavMapAdd(t *testing.T) { nM := NewNavigableMap(nil) path := []string{"FistLever2", "SecondLevel2", "Field2"} data := "Value2" - nM.Set(&NMItem{Path: path, Data: data}, true) + nM.Set(path, data, true) path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"} data = "Val1" - nM.Set(&NMItem{Path: path, Data: data}, true) + nM.Set(path, data, true) path = []string{"FistLever2", "Field3"} data = "Value3" - nM.Set(&NMItem{Path: path, Data: data}, true) + nM.Set(path, data, true) path = []string{"Field4"} data = "Val4" - nM.Set(&NMItem{Path: path, Data: data}, true) + nM.Set(path, data, 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"}, + "Fld1": "Val1", }, }, }, "FistLever2": map[string]interface{}{ "SecondLevel2": map[string]interface{}{ - "Field2": &NMItem{Path: []string{"FistLever2", "SecondLevel2", "Field2"}, - Data: "Value2"}, + "Field2": "Value2", }, - "Field3": &NMItem{Path: []string{"FistLever2", "Field3"}, - Data: "Value3"}, + "Field3": "Value3", }, - "Field4": &NMItem{Path: []string{"Field4"}, - Data: "Val4"}, + "Field4": "Val4", }, } if !reflect.DeepEqual(nM.data, eNavMap.data) { @@ -194,42 +193,38 @@ func TestNavMapAdd2(t *testing.T) { nM := NewNavigableMap(nil) path := []string{"FistLever2", "SecondLevel2", "Field2"} data := 123 - nM.Set(&NMItem{Path: path, Data: data}, true) + nM.Set(path, data, true) path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"} data1 := 123.123 - nM.Set(&NMItem{Path: path, Data: data1}, true) + nM.Set(path, data1, true) path = []string{"FistLever2", "Field3"} data2 := "Value3" - nM.Set(&NMItem{Path: path, Data: data2}, true) + nM.Set(path, data2, true) path = []string{"Field4"} data3 := &testStruct{ Item1: "Ten", Item2: 10, } - nM.Set(&NMItem{Path: path, Data: data3}, true) + nM.Set(path, data3, 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: 123.123}, + "Fld1": 123.123, }, }, }, "FistLever2": map[string]interface{}{ "SecondLevel2": map[string]interface{}{ - "Field2": &NMItem{Path: []string{"FistLever2", "SecondLevel2", "Field2"}, - Data: 123}, + "Field2": 123, }, - "Field3": &NMItem{Path: []string{"FistLever2", "Field3"}, - Data: "Value3"}, + "Field3": "Value3", + }, + "Field4": &testStruct{ + Item1: "Ten", + Item2: 10, }, - "Field4": &NMItem{Path: []string{"Field4"}, - Data: &testStruct{ - Item1: "Ten", - Item2: 10, - }}, }, } if !reflect.DeepEqual(nM.data, eNavMap.data) { @@ -273,8 +268,9 @@ func TestNavMapItems(t *testing.T) { Data: "Val4", }, } - if !reflect.DeepEqual(len(nM.Items()), len(eItems)) { - t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eItems), utils.ToJSON(nM.Items())) + if vals := nM.Values(); !reflect.DeepEqual(len(vals), len(eItems)) { + t.Errorf("Expecting: %+v, received: %+v", + utils.ToJSON(eItems), utils.ToJSON(vals)) } } @@ -320,8 +316,9 @@ func TestNavMapItems2(t *testing.T) { }, }, } - if !reflect.DeepEqual(len(nM.Items()), len(eItems)) { - t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eItems), utils.ToJSON(nM.Items())) + if vals := nM.Values(); !reflect.DeepEqual(len(vals), len(eItems)) { + t.Errorf("Expecting: %+v, received: %+v", + utils.ToJSON(eItems), utils.ToJSON(vals)) } } @@ -330,17 +327,29 @@ func TestNavMapOrder(t *testing.T) { "FirstLevel": map[string]interface{}{ "SecondLevel": map[string]interface{}{ "ThirdLevel": map[string]interface{}{ - "Fld1": "Val1", + "Fld1": &NMItem{ + Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, + Data: "Val1", + }, }, }, }, "FistLever2": map[string]interface{}{ "SecondLevel2": map[string]interface{}{ - "Field2": "Value2", + "Field2": &NMItem{ + Path: []string{"FistLever2", "SecondLevel2", "Field2"}, + Data: "Value2", + }, + }, + "Field3": &NMItem{ + Path: []string{"FistLever2", "Field3"}, + Data: "Value3", }, - "Field3": "Value3", }, - "Field4": "Val4", + "Field4": &NMItem{ + Path: []string{"Field4"}, + Data: "Val4", + }, } order := [][]string{ []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, @@ -350,7 +359,7 @@ func TestNavMapOrder(t *testing.T) { } nM := NewNavigableMap(myData) nM.order = order - eItems := []*NMItem{ + eItems := []interface{}{ &NMItem{ Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, Data: "Val1", @@ -368,8 +377,9 @@ func TestNavMapOrder(t *testing.T) { Data: "Val4", }, } - if !reflect.DeepEqual(nM.Items(), eItems) { - t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eItems), utils.ToJSON(nM.Items())) + if vals := nM.Values(); !reflect.DeepEqual(vals, eItems) { + t.Errorf("Expecting: %+v, received: %+v", + utils.ToJSON(eItems), utils.ToJSON(vals)) } } @@ -378,17 +388,29 @@ func TestNavMapOrder2(t *testing.T) { "FirstLevel": map[string]interface{}{ "SecondLevel": map[string]interface{}{ "ThirdLevel": map[string]interface{}{ - "Fld1": "Val1", + "Fld1": &NMItem{ + Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, + Data: "Val1", + }, }, }, }, "FistLever2": map[string]interface{}{ "SecondLevel2": map[string]interface{}{ - "Field2": "Value2", + "Field2": &NMItem{ + Path: []string{"FistLever2", "SecondLevel2", "Field2"}, + Data: "Value2", + }, + }, + "Field3": &NMItem{ + Path: []string{"FistLever2", "Field3"}, + Data: "Value3", }, - "Field3": "Value3", }, - "Field4": "Val4", + "Field4": &NMItem{ + Path: []string{"Field4"}, + Data: "Val4", + }, } order := [][]string{ []string{"FistLever2", "SecondLevel2", "Field2"}, @@ -398,7 +420,7 @@ func TestNavMapOrder2(t *testing.T) { } nM := NewNavigableMap(myData) nM.order = order - eItems := []*NMItem{ + eItems := []interface{}{ &NMItem{ Path: []string{"FistLever2", "SecondLevel2", "Field2"}, Data: "Value2", @@ -416,13 +438,14 @@ func TestNavMapOrder2(t *testing.T) { Data: "Val1", }, } - if !reflect.DeepEqual(nM.Items(), eItems) { - t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eItems), utils.ToJSON(nM.Items())) + 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 []*NMItem + var elmsOut []interface{} ifaceMap := map[string]interface{}{ "FirstLevel": map[string]interface{}{ "SecondLevel": map[string]interface{}{ @@ -439,24 +462,7 @@ func TestNavMapIndexMapElementes(t *testing.T) { }, "Field4": "Val4", } - 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", - }, - } + 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)) @@ -486,3 +492,44 @@ func TestNavMapString(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eStr, nM.String()) } } + +func TestNavMapMarshalXML(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"}}, + }, + }, + }, + "FistLever2": map[string]interface{}{ + "SecondLevel2": map[string]interface{}{ + "Field2": []*NMItem{ + &NMItem{Path: []string{"FistLever2", "SecondLevel2", "Field2"}, + Data: "Value2", + Config: &config.CfgCdrField{Tag: "AttributeTest", AttributeID: "attribute1"}}}, + }, + "Field3": []*NMItem{ + &NMItem{Path: []string{"FistLever2", "Field3"}, + Data: "Value3"}}, + }, + "Field4": []*NMItem{ + &NMItem{Path: []string{"Field4"}, + Data: "Val4"}}, + }, + order: [][]string{ + []string{"FistLever2", "SecondLevel2", "Field2"}, + []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, + []string{"FistLever2", "Field3"}, + []string{"Field4"}, + }, + } + if output, err := xml.MarshalIndent(nm, "", " "); err != nil { + t.Error(err) + } else if !reflect.DeepEqual([]byte(""), output) { + //fmt.Printf("received output: <%s>\n", output) + } +}