diff --git a/agents/dmtagent.go b/agents/dmtagent.go
index 778197cdb..641191800 100644
--- a/agents/dmtagent.go
+++ b/agents/dmtagent.go
@@ -174,12 +174,15 @@ func (da DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProce
if ccr.CCRequestType == 3 {
if err = da.sessionS.Call(utils.SessionSv1TerminateSession,
procVars.asV1TerminateSessionArgs(cgrEv), &rpl); err != nil {
- procVars[utils.MetaCGRReply] = utils.CGRReply{utils.Error: err.Error()}
+ procVars[utils.MetaCGRReply] = map[string]interface{}{utils.Error: err.Error()}
}
} else if ccr.CCRequestType == 4 {
var evntRply sessions.V1ProcessEventReply
err = da.sessionS.Call(utils.SessionSv1ProcessEvent,
procVars.asV1ProcessEventArgs(cgrEv), &evntRply)
+ if evntRply.MaxUsage != nil && *evntRply.MaxUsage == 0 {
+ cgrEv.Event[utils.Usage] = 0 // prevent CDR to be written
+ }
if procVars[utils.MetaCGRReply], err = utils.NewCGRReply(&evntRply, err); err != nil {
return
}
@@ -189,7 +192,7 @@ func (da DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProce
!strings.HasSuffix(err.Error(), utils.ErrNoActiveSession.Error())) { // Check if CDR requires session
if errCdr := da.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rpl); errCdr != nil {
err = errCdr
- procVars[utils.MetaCGRReply] = utils.CGRReply{utils.Error: err.Error()}
+ procVars[utils.MetaCGRReply] = map[string]interface{}{utils.Error: err.Error()}
}
}
}
diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go
index 49b075cc3..e945e2456 100644
--- a/agents/libdmt_test.go
+++ b/agents/libdmt_test.go
@@ -226,7 +226,7 @@ func TestFieldOutVal(t *testing.T) {
FieldFilter: utils.ParseRSRFieldsMustCompile("*cgrReply>Error(^$);*cgrReply>MaxUsage(!300);*cgrReply>MaxUsage(!0)", utils.INFIELD_SEP),
Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}
procVars := processorVars{
- utils.MetaCGRReply: utils.CGRReply{
+ utils.MetaCGRReply: utils.NavigableMap{
utils.Error: "RALS_ERROR:NOT_FOUND",
},
}
@@ -526,12 +526,13 @@ func TestPassesFieldFilter(t *testing.T) {
t.Error("Does not pass")
}
procVars := processorVars{
- utils.MetaCGRReply: utils.CGRReply{
+ utils.MetaCGRReply: map[string]interface{}{
utils.CapAttributes: map[string]interface{}{
"RadReply": "AccessAccept",
utils.Account: "1001",
},
utils.CapMaxUsage: time.Duration(0),
+ utils.Error: "",
},
}
if pass, _ := passesFieldFilter(nil,
@@ -544,4 +545,9 @@ func TestPassesFieldFilter(t *testing.T) {
procVars); !pass {
t.Error("not passing valid filter")
}
+ if pass, _ := passesFieldFilter(nil,
+ utils.ParseRSRFieldsMustCompile("*cgrReply>Error(^$)", utils.INFIELD_SEP)[0],
+ procVars); !pass {
+ t.Error("not passing valid filter")
+ }
}
diff --git a/agents/librad.go b/agents/librad.go
index 95d51f580..087b1088c 100644
--- a/agents/librad.go
+++ b/agents/librad.go
@@ -61,11 +61,7 @@ func (pv processorVars) valAsInterface(fldPath string) (val interface{}, err err
err = errors.New("not found")
return
}
- if fldName == utils.MetaCGRReply {
- cgrRply := pv[utils.MetaCGRReply].(utils.CGRReply)
- return cgrRply.GetField(fldPath, utils.HIERARCHY_SEP)
- }
- return pv[fldName], nil
+ return utils.NavigableMap(pv).GetField(fldPath, utils.HIERARCHY_SEP)
}
// valAsString returns the string value for fldName
@@ -78,17 +74,7 @@ func (pv processorVars) valAsString(fldPath string) (val string, err error) {
if !pv.hasVar(fldName) {
return "", utils.ErrNotFoundNoCaps
}
- if fldName == utils.MetaCGRReply {
- cgrRply := pv[utils.MetaCGRReply].(utils.CGRReply)
- return cgrRply.GetFieldAsString(fldPath, utils.HIERARCHY_SEP)
- }
- if valIface, hasIt := pv[fldName]; hasIt {
- var canCast bool
- if val, canCast = utils.CastFieldIfToString(valIface); !canCast {
- return "", fmt.Errorf("cannot cast field <%s> to string", fldPath)
- }
- }
- return
+ return utils.NavigableMap(pv).GetFieldAsString(fldPath, utils.HIERARCHY_SEP)
}
// asV1AuthorizeArgs returns the arguments needed by SessionSv1.AuthorizeEvent
diff --git a/agents/librad_test.go b/agents/librad_test.go
index e3d5d0c9d..c288b719b 100644
--- a/agents/librad_test.go
+++ b/agents/librad_test.go
@@ -404,7 +404,7 @@ func TestRadReplyAppendAttributes(t *testing.T) {
Value: utils.ParseRSRFieldsMustCompile("*cgrReply>MaxUsage{*duration_seconds}", utils.INFIELD_SEP)},
}
procVars := make(processorVars)
- procVars[utils.MetaCGRReply] = utils.CGRReply{
+ procVars[utils.MetaCGRReply] = map[string]interface{}{
utils.CapAttributes: map[string]interface{}{
"RadReply": "AccessAccept",
utils.Account: "1001",
diff --git a/agents/radagent.go b/agents/radagent.go
index cc44e6e2f..30e8af91d 100644
--- a/agents/radagent.go
+++ b/agents/radagent.go
@@ -195,12 +195,12 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor,
var rpl string
if err = ra.sessionS.Call(utils.SessionSv1TerminateSession,
procVars.asV1TerminateSessionArgs(cgrEv), &rpl); err != nil {
- procVars[utils.MetaCGRReply] = utils.CGRReply{utils.Error: err.Error()}
+ procVars[utils.MetaCGRReply] = map[string]interface{}{utils.Error: err.Error()}
}
if ra.cgrCfg.RadiusAgentCfg().CreateCDR {
if errCdr := ra.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rpl); errCdr != nil {
err = errCdr
- procVars[utils.MetaCGRReply] = utils.CGRReply{utils.Error: err.Error()}
+ procVars[utils.MetaCGRReply] = map[string]interface{}{utils.Error: err.Error()}
}
}
if err != nil {
diff --git a/sessions/sessions.go b/sessions/sessions.go
index 4e40fd9d4..4e612ea3e 100644
--- a/sessions/sessions.go
+++ b/sessions/sessions.go
@@ -1327,7 +1327,7 @@ type V1AuthorizeReply struct {
}
// AsCGRReply is part of utils.CGRReplier interface
-func (v1AuthReply *V1AuthorizeReply) AsCGRReply() (cgrReply utils.CGRReply, err error) {
+func (v1AuthReply *V1AuthorizeReply) AsNavigableMap() (cgrReply map[string]interface{}, err error) {
cgrReply = make(map[string]interface{})
if v1AuthReply.Attributes != nil {
attrs := make(map[string]interface{})
@@ -1542,7 +1542,7 @@ type V1InitSessionReply struct {
}
// AsCGRReply is part of utils.CGRReplier interface
-func (v1Rply *V1InitSessionReply) AsCGRReply() (cgrReply utils.CGRReply, err error) {
+func (v1Rply *V1InitSessionReply) AsNavigableMap() (cgrReply map[string]interface{}, err error) {
cgrReply = make(map[string]interface{})
if v1Rply.Attributes != nil {
attrs := make(map[string]interface{})
@@ -1722,7 +1722,7 @@ type V1UpdateSessionReply struct {
}
// AsCGRReply is part of utils.CGRReplier interface
-func (v1Rply *V1UpdateSessionReply) AsCGRReply() (cgrReply utils.CGRReply, err error) {
+func (v1Rply *V1UpdateSessionReply) AsNavigableMap() (cgrReply map[string]interface{}, err error) {
cgrReply = make(map[string]interface{})
if v1Rply.Attributes != nil {
attrs := make(map[string]interface{})
@@ -1889,7 +1889,7 @@ type V1ProcessEventReply struct {
}
// AsCGRReply is part of utils.CGRReplier interface
-func (v1Rply *V1ProcessEventReply) AsCGRReply() (cgrReply utils.CGRReply, err error) {
+func (v1Rply *V1ProcessEventReply) AsNavigableMap() (cgrReply map[string]interface{}, err error) {
cgrReply = make(map[string]interface{})
if v1Rply.MaxUsage != nil {
cgrReply[utils.CapMaxUsage] = *v1Rply.MaxUsage
diff --git a/utils/cgrreply.go b/utils/cgrreply.go
deleted file mode 100644
index ae39f05aa..000000000
--- a/utils/cgrreply.go
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-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 PURPOev. 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"
- "strings"
-)
-
-// CGRReplier is the interface supported by replies convertible to CGRReply
-type CGRReplier interface {
- AsCGRReply() (CGRReply, error)
-}
-
-func NewCGRReply(rply CGRReplier, errRply error) (cgrReply CGRReply, err error) {
- if errRply != nil {
- return CGRReply{Error: errRply.Error()}, nil
- }
- if cgrReply, err = rply.AsCGRReply(); err != nil {
- return
- }
- cgrReply[Error] = "" // enforce empty error
- return
-}
-
-// CGRReply represents the CGRateS answer which can be used in templates
-// it can be layered, case when interface{} will be castable into map[string]interface{}
-type CGRReply map[string]interface{}
-
-// GetField returns the field value as interface{} for the path specified
-func (cgrReply CGRReply) GetField(fldPath string, sep string) (fldVal interface{}, err error) {
- path := strings.Split(fldPath, sep)
- lenPath := len(path)
- if lenPath == 0 {
- err = errors.New("empty field path")
- return
- }
- if path[0] == MetaCGRReply {
- path = path[1:]
- lenPath -= 1
- }
- lastMp := cgrReply // last map when layered
- var canCast bool
- for i, spath := range path {
- if i == lenPath-1 { // lastElement
- var has bool
- fldVal, has = lastMp[spath]
- if !has {
- err = fmt.Errorf("no field with path: <%s>", fldPath)
- return
- }
- return
- } else {
- lastMp, canCast = lastMp[spath].(map[string]interface{})
- if !canCast {
- err = fmt.Errorf("cannot cast field: %s to map[string]interface{}", ToJSON(lastMp[spath]))
- return
- }
- }
- }
- err = errors.New("end of function")
- return
-}
-
-// GetFieldAsString returns the field value as string for the path specified
-func (cgrReply CGRReply) GetFieldAsString(fldPath string, sep string) (fldVal string, err error) {
- var valIface interface{}
- valIface, err = cgrReply.GetField(fldPath, sep)
- if err != nil {
- return
- }
- var canCast bool
- if fldVal, canCast = CastFieldIfToString(valIface); !canCast {
- return "", fmt.Errorf("cannot cast field: %s to string", ToJSON(valIface))
- }
- return
-}
diff --git a/utils/cgrreply_test.go b/utils/cgrreply_test.go
deleted file mode 100644
index 8381d479c..000000000
--- a/utils/cgrreply_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-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"
- "reflect"
- "testing"
-)
-
-type myEv map[string]interface{}
-
-func (ev myEv) AsCGRReply() (CGRReply, error) {
- return CGRReply(ev), nil
-}
-
-func TestCGRReplyNew(t *testing.T) {
- eCgrRply := CGRReply(map[string]interface{}{
- Error: "some",
- })
- if rpl, err := NewCGRReply(nil, errors.New("some")); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eCgrRply, rpl) {
- t.Errorf("Expecting: %+v, received: %+v", ToJSON(eCgrRply), ToJSON(rpl))
- }
- ev := myEv{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- }
- eCgrRply = CGRReply(ev)
- eCgrRply[Error] = ""
- if rpl, err := NewCGRReply(CGRReplier(ev), nil); err != nil {
- t.Error(err)
- } else if !reflect.DeepEqual(eCgrRply, rpl) {
- t.Errorf("Expecting: %+v, received: %+v", eCgrRply, rpl)
- }
-}
-
-func TestCGRReplyGetFieldAsString(t *testing.T) {
- ev := myEv{
- "FirstLevel": map[string]interface{}{
- "SecondLevel": map[string]interface{}{
- "ThirdLevel": map[string]interface{}{
- "Fld1": "Val1",
- },
- },
- },
- "AnotherFirstLevel": "ValAnotherFirstLevel",
- }
- cgrRply, _ := NewCGRReply(CGRReplier(ev), nil)
- if strVal, err := cgrRply.GetFieldAsString("*cgrReply>Error", ">"); err != nil {
- t.Error(err)
- } else if strVal != "" {
- t.Errorf("received: <%s>", strVal)
- }
- eVal := "Val1"
- if strVal, err := cgrRply.GetFieldAsString("*cgrReply>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 := cgrRply.GetFieldAsString("*cgrReply>AnotherFirstLevel", ">"); err != nil {
- t.Error(err)
- } else if strVal != eVal {
- t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
- }
-}
diff --git a/utils/navigablemap.go b/utils/navigablemap.go
index a6612649a..1884f0533 100644
--- a/utils/navigablemap.go
+++ b/utils/navigablemap.go
@@ -24,6 +24,11 @@ import (
"strings"
)
+// CGRReplier is the interface supported by replies convertible to CGRReply
+type NavigableMapper interface {
+ AsNavigableMap() (map[string]interface{}, error)
+}
+
// NavigableMap is a map who's values can be navigated via path
type NavigableMap map[string]interface{}
@@ -53,7 +58,7 @@ func (nM NavigableMap) GetField(fldPath string, sep string) (fldVal interface{},
}
lastMp, canCast = elmnt.(map[string]interface{})
if !canCast {
- err = fmt.Errorf("cannot cast field: %s to map[string]interface{}", ToJSON(lastMp[spath]))
+ err = fmt.Errorf("cannot cast field: %s to map[string]interface{}", ToJSON(elmnt))
return
}
}
@@ -75,3 +80,15 @@ func (nM NavigableMap) GetFieldAsString(fldPath string, sep string) (fldVal stri
}
return
}
+
+// NewCGRReply is specific to replies coming from CGRateS
+func NewCGRReply(rply NavigableMapper, errRply error) (nM map[string]interface{}, err error) {
+ if errRply != nil {
+ return NavigableMap{Error: errRply.Error()}, nil
+ }
+ if nM, err = rply.AsNavigableMap(); err != nil {
+ return
+ }
+ nM[Error] = "" // enforce empty error
+ return
+}
diff --git a/utils/navigablemap_test.go b/utils/navigablemap_test.go
index 6575ea924..31172cd88 100644
--- a/utils/navigablemap_test.go
+++ b/utils/navigablemap_test.go
@@ -19,6 +19,7 @@ package utils
import (
"errors"
+ "reflect"
"testing"
)
@@ -50,3 +51,34 @@ func TestNavMapGetFieldAsString(t *testing.T) {
t.Error(err)
}
}
+
+type myEv map[string]interface{}
+
+func (ev myEv) AsNavigableMap() (map[string]interface{}, error) {
+ return NavigableMap(ev), nil
+}
+
+func TestCGRReplyNew(t *testing.T) {
+ eCgrRply := map[string]interface{}{
+ Error: "some",
+ }
+ if rpl, err := NewCGRReply(nil, errors.New("some")); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eCgrRply, rpl) {
+ t.Errorf("Expecting: %+v, received: %+v", ToJSON(eCgrRply), ToJSON(rpl))
+ }
+ ev := myEv{
+ "FirstLevel": map[string]interface{}{
+ "SecondLevel": map[string]interface{}{
+ "Fld1": "Val1",
+ },
+ },
+ }
+ eCgrRply = ev
+ eCgrRply[Error] = ""
+ if rpl, err := NewCGRReply(NavigableMapper(ev), nil); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eCgrRply, rpl) {
+ t.Errorf("Expecting: %+v, received: %+v", eCgrRply, rpl)
+ }
+}