From 44dedd787581d4520cb6060adb14a96f32fad750 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 29 Jun 2018 19:41:52 +0200 Subject: [PATCH] NMItem with support for Config attached, NavigableMap.Set with NMItem --- agents/agentreq.go | 5 +-- agents/agentreq_test.go | 30 ++++++++-------- agents/httpagent.go | 3 +- agents/libhttpagent.go | 2 +- agents/librad.go | 2 +- agents/librad_test.go | 2 +- engine/navigablemap.go | 71 ++++++++++++++++++++++++++++--------- engine/navigablemap_test.go | 46 ++++++++++++++---------- 8 files changed, 105 insertions(+), 56 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index da295464d..9b52aa8c4 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -116,8 +116,9 @@ func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.CfgCdrField) ( if err != nil { return nil, err } - nM.Set(strings.Split(tplFld.FieldId, - utils.HIERARCHY_SEP), out, true) + nM.Set( + &engine.NMItem{Path: strings.Split(tplFld.FieldId, + utils.HIERARCHY_SEP), Data: out}, true) } return } diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go index b44b689ec..37882581a 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([]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) + 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) 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([]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) + 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) 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 856c5639b..9f3e68f17 100644 --- a/agents/httpagent.go +++ b/agents/httpagent.go @@ -211,7 +211,8 @@ func (ha *HTTPAgent) processRequest(reqProcessor *config.HttpAgntProcCfg, var rplyCDRs string if err = ha.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rplyCDRs); err != nil { - agReq.CGRReply.Set([]string{utils.Error}, err.Error(), false) + agReq.CGRReply.Set( + &engine.NMItem{Path: []string{utils.Error}, Data: err.Error()}, false) } } if nM, err := agReq.AsNavigableMap(reqProcessor.ReplyFields); err != nil { diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go index 0054f9987..9969e53e7 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(fldPath, data, false) + hU.cache.Set(&engine.NMItem{Path: fldPath, Data: data}, false) return } diff --git a/agents/librad.go b/agents/librad.go index 0cf2f9d4e..fec330555 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([]string{utils.Error}, "", false) // enforce empty error + mp.Set(&engine.NMItem{Path: []string{utils.Error}, Data: ""}, false) // enforce empty error return mp, nil } diff --git a/agents/librad_test.go b/agents/librad_test.go index 89d0b7ed3..5a7bbf2d0 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([]string{utils.Error}, "", false) + eCgrRply.Set(&engine.NMItem{Path: []string{utils.Error}, Data: ""}, false) if rpl, err := NewCGRReply(engine.NavigableMapper(ev), nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCgrRply, rpl) { diff --git a/engine/navigablemap.go b/engine/navigablemap.go index 43659f1f4..a236299e8 100644 --- a/engine/navigablemap.go +++ b/engine/navigablemap.go @@ -39,6 +39,13 @@ func NewNavigableMap(data map[string]interface{}) *NavigableMap { return &NavigableMap{data: data} } +// NMItem is an item in the NavigableMap +type NMItem struct { + Path []string // path in map + Data interface{} // value of the element + Config *config.CfgCdrField // so we can store additional configuration +} + // 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 @@ -48,11 +55,11 @@ type NavigableMap struct { } // Add will add items into NavigableMap populating also order -func (nM *NavigableMap) Set(path []string, data interface{}, ordered bool) { +func (nM *NavigableMap) Set(itm *NMItem, ordered bool) { mp := nM.data - for i, spath := range path { - if i == len(path)-1 { // last path - mp[spath] = data + for i, spath := range itm.Path { + if i == len(itm.Path)-1 { // last path + mp[spath] = itm return } if _, has := mp[spath]; !has { @@ -61,7 +68,7 @@ func (nM *NavigableMap) Set(path []string, data interface{}, ordered bool) { mp = mp[spath].(map[string]interface{}) // so we can check further down } if ordered { - nM.order = append(nM.order, path) + nM.order = append(nM.order, itm.Path) } } @@ -76,12 +83,14 @@ func (nM *NavigableMap) FieldAsInterface(fldPath []string) (fldVal interface{}, var canCast bool for i, spath := range fldPath { if i == lenPath-1 { // lastElement - var has bool - fldVal, has = lastMp[spath] + itmIface, has := lastMp[spath] if !has { return nil, utils.ErrNotFound } - return + if itm, cast := itmIface.(*NMItem); cast { + return itm.Data, nil + } + return itmIface, nil } else { elmnt, has := lastMp[spath] if !has { @@ -128,21 +137,22 @@ func (nM *NavigableMap) AsMapStringInterface() map[string]interface{} { return nM.data } -type NMItem struct { - Path []string // path in map - Data interface{} // value of the element -} - // indexMapElements will recursively go through map and index the element paths into elmns func indexMapElements(mp map[string]interface{}, path []string, elms *[]*NMItem) { for k, v := range mp { vPath := append(path, k) if mpIface, isMap := v.(map[string]interface{}); isMap { indexMapElements(mpIface, vPath, elms) - } else { - elmsOut := append(*elms, &NMItem{vPath, v}) - *elms = elmsOut + 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 } } @@ -168,6 +178,7 @@ func (nM *NavigableMap) AsNavigableMap(tpl []*config.CfgCdrField) (oNM *Navigabl return nil, utils.ErrNotImplemented } +// Merge will update nM with values from a second one func (nM *NavigableMap) Merge(nM2 *NavigableMap) { if nM2 == nil { return @@ -179,3 +190,31 @@ func (nM *NavigableMap) Merge(nM2 *NavigableMap) { nM.order = append(nM.order, nM2.order...) } } + +/* +// MarshalXML implements xml.Marshaler +func (nM *NavigableMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + tokens := []xml.Token{start} + for _, itm := range nM.Items() { + t := xml.StartElement{Name: xml.Name{"", strings.Join(itm.Path, ">")}} + tokens = append(tokens, t, xml.CharData(value), xml.EndElement{t.Name}) + } + + tokens = append(tokens, xml.EndElement{start.Name}) + + for _, t := range tokens { + err := e.EncodeToken(t) + if err != nil { + return err + } + } + + // flush to ensure tokens are written + err := e.Flush() + if err != nil { + return err + } + + return nil +} +*/ diff --git a/engine/navigablemap_test.go b/engine/navigablemap_test.go index 698782f83..8850fc27d 100644 --- a/engine/navigablemap_test.go +++ b/engine/navigablemap_test.go @@ -148,32 +148,36 @@ func TestNavMapAdd(t *testing.T) { nM := NewNavigableMap(nil) path := []string{"FistLever2", "SecondLevel2", "Field2"} data := "Value2" - nM.Set(path, data, true) + nM.Set(&NMItem{Path: path, Data: data}, true) path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"} data = "Val1" - nM.Set(path, data, true) + nM.Set(&NMItem{Path: path, Data: data}, true) path = []string{"FistLever2", "Field3"} data = "Value3" - nM.Set(path, data, true) + nM.Set(&NMItem{Path: path, Data: data}, true) path = []string{"Field4"} data = "Val4" - nM.Set(path, data, true) + nM.Set(&NMItem{Path: path, Data: data}, true) eNavMap := NavigableMap{ data: map[string]interface{}{ "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": "Value3", + "Field3": &NMItem{Path: []string{"FistLever2", "Field3"}, + Data: "Value3"}, }, - "Field4": "Val4", + "Field4": &NMItem{Path: []string{"Field4"}, + Data: "Val4"}, }, } if !reflect.DeepEqual(nM.data, eNavMap.data) { @@ -190,38 +194,42 @@ func TestNavMapAdd2(t *testing.T) { nM := NewNavigableMap(nil) path := []string{"FistLever2", "SecondLevel2", "Field2"} data := 123 - nM.Set(path, data, true) + nM.Set(&NMItem{Path: path, Data: data}, true) path = []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"} data1 := 123.123 - nM.Set(path, data1, true) + nM.Set(&NMItem{Path: path, Data: data1}, true) path = []string{"FistLever2", "Field3"} data2 := "Value3" - nM.Set(path, data2, true) + nM.Set(&NMItem{Path: path, Data: data2}, true) path = []string{"Field4"} data3 := &testStruct{ Item1: "Ten", Item2: 10, } - nM.Set(path, data3, true) + nM.Set(&NMItem{Path: path, Data: data3}, true) eNavMap := NavigableMap{ data: map[string]interface{}{ "FirstLevel": map[string]interface{}{ "SecondLevel": map[string]interface{}{ "ThirdLevel": map[string]interface{}{ - "Fld1": 123.123, + "Fld1": &NMItem{Path: []string{"FirstLevel", "SecondLevel", "ThirdLevel", "Fld1"}, + Data: 123.123}, }, }, }, "FistLever2": map[string]interface{}{ "SecondLevel2": map[string]interface{}{ - "Field2": 123, + "Field2": &NMItem{Path: []string{"FistLever2", "SecondLevel2", "Field2"}, + Data: 123}, }, - "Field3": "Value3", - }, - "Field4": &testStruct{ - Item1: "Ten", - Item2: 10, + "Field3": &NMItem{Path: []string{"FistLever2", "Field3"}, + Data: "Value3"}, }, + "Field4": &NMItem{Path: []string{"Field4"}, + Data: &testStruct{ + Item1: "Ten", + Item2: 10, + }}, }, } if !reflect.DeepEqual(nM.data, eNavMap.data) {