mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
DiameterAgent forcing usage 0 when debit not possible, replacing CGRReply with NavigableMap
This commit is contained in:
@@ -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()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user