NMItem with support for Config attached, NavigableMap.Set with NMItem

This commit is contained in:
DanB
2018-06-29 19:41:52 +02:00
parent 31ca9de5d1
commit 44dedd7875
8 changed files with 105 additions and 56 deletions

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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
}
*/

View File

@@ -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) {