Initial AgentRequest with SetFields

This commit is contained in:
DanB
2020-01-31 10:29:26 +01:00
parent 7d848069ef
commit 8dbd7ea2cd
4 changed files with 161 additions and 17 deletions

View File

@@ -83,6 +83,7 @@ type AgentRequest struct {
filterS *engine.FilterS
Header config.DataProvider
Trailer config.DataProvider
diamreq *config.NavigableMap // used in case of building requests (ie. DisconnectSession)
}
// String implements engine.DataProvider
@@ -103,23 +104,18 @@ func (ar *AgentRequest) FieldAsInterface(fldPath []string) (val interface{}, err
case utils.MetaReq:
val, err = ar.Request.FieldAsInterface(fldPath[1:])
case utils.MetaVars:
val, err = ar.Vars.FieldAsInterface(fldPath[1:])
val, err = ar.Vars.GetField(fldPath[1:])
case utils.MetaCgreq:
val, err = ar.CGRRequest.FieldAsInterface(fldPath[1:])
val, err = ar.CGRRequest.GetField(fldPath[1:])
case utils.MetaCgrep:
val, err = ar.CGRReply.FieldAsInterface(fldPath[1:])
val, err = ar.CGRReply.GetField(fldPath[1:])
case utils.MetaRep:
val, err = ar.Reply.FieldAsInterface(fldPath[1:])
case utils.MetaCGRAReq:
val, err = ar.CGRAReq.FieldAsInterface(fldPath[1:])
val, err = ar.Reply.GetField(fldPath[1:])
case utils.MetaHdr:
val, err = ar.Header.FieldAsInterface(fldPath[1:])
case utils.MetaTrl:
val, err = ar.Trailer.FieldAsInterface(fldPath[1:])
}
if nmItems, isNMItems := val.([]*config.NMItem); isNMItems { // special handling of NMItems, take the last value out of it
val = nmItems[len(nmItems)-1].Data // could be we need nil protection here
}
return
}
@@ -129,17 +125,24 @@ func (ar *AgentRequest) FieldAsString(fldPath []string) (val string, err error)
if iface, err = ar.FieldAsInterface(fldPath); err != nil {
return
}
if nmItems, isNMItems := iface.([]*config.NMItem); isNMItems { // special handling of NMItems, take the last value out of it
iface = nmItems[len(nmItems)-1].Data // could be we need nil protection here
}
return utils.IfaceAsString(iface), nil
}
// AsNavigableMap implements engine.DataProvider
func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.FCTemplate) (
nM *config.NavigableMap, err error) {
ar.CGRAReq = config.NewNavigableMap(nil)
return nil, utils.ErrNotImplemented
}
//SetFields will populate fields of AgentRequest out of templates
func (ar *AgentRequest) SetFields(tplFlds []*config.FCTemplate) (err error) {
for _, tplFld := range tplFlds {
if pass, err := ar.filterS.Pass(ar.Tenant,
tplFld.Filters, ar); err != nil {
return nil, err
return err
} else if !pass {
continue
}
@@ -153,15 +156,15 @@ func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.FCTemplate) (
}
err = utils.ErrPrefixNotFound(tplFld.Tag)
}
return nil, err
return err
}
var valSet []*config.NMItem
fldPath := strings.Split(tplFld.Path, utils.NestingSep)
nMItm := &config.NMItem{Data: out, Path: fldPath, Config: tplFld}
if nMFields, err := ar.CGRAReq.FieldAsInterface(fldPath); err != nil {
nMItm := &config.NMItem{Data: out, Path: fldPath[1:], Config: tplFld}
if nMFields, err := ar.FieldAsInterface(fldPath); err != nil {
if err != utils.ErrNotFound {
return nil, err
return err
}
} else {
valSet = nMFields.([]*config.NMItem) // start from previous stored fields
@@ -173,13 +176,29 @@ func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.FCTemplate) (
valSet = valSet[:len(valSet)-1] // discard the last item
}
valSet = append(valSet, nMItm)
ar.CGRAReq.Set(fldPath, valSet, false, true)
switch fldPath[0] {
default:
return fmt.Errorf("unsupported field prefix: <%s> when set fields", fldPath[0])
case utils.MetaVars:
ar.Vars.Set(fldPath[1:], valSet, false, true)
case utils.MetaCgreq:
ar.CGRRequest.Set(fldPath[1:], valSet, false, true)
case utils.MetaCgrep:
ar.CGRReply.Set(fldPath[1:], valSet, false, true)
case utils.MetaRep:
ar.Reply.Set(fldPath[1:], valSet, false, true)
case utils.MetaDiamreq:
if ar.diamreq == nil {
ar.diamreq = config.NewNavigableMap(nil) // special case when CGRateS is building the request
}
ar.diamreq.Set(fldPath[1:], valSet, false, true)
}
}
if tplFld.Blocker { // useful in case of processing errors first
break
}
}
return ar.CGRAReq, nil
return
}
// ParseField outputs the value based on the template item

View File

@@ -93,6 +93,59 @@ func (nM *NavigableMap) Set(path []string, data interface{}, apnd, ordered bool)
}
}
// 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)
}
if lastMp, err = nM.getNextMap(lastMp, spath); err != nil {
return
}
}
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 DataProvider
// supports spath with selective elements in case of []*NMItem

View File

@@ -997,6 +997,77 @@ func TestNavMapgetLastItem(t *testing.T) {
}
}
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{}{

View File

@@ -621,6 +621,7 @@ const (
MaxCost = "MaxCost"
MetaLoaders = "*loaders"
TmpSuffix = ".tmp"
MetaDiamreq = "*diamreq"
)
// Migrator Action