mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Diameter ccr_fields and cca_fields inside processors
This commit is contained in:
@@ -68,14 +68,14 @@ func (self *DiameterAgent) handlers() diam.Handler {
|
||||
func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor) (*CCA, error) {
|
||||
passesAllFilters := true
|
||||
for _, fldFilter := range reqProcessor.RequestFilter {
|
||||
if passes, _ := ccr.passesFieldFilter(fldFilter); !passes {
|
||||
if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter); !passes {
|
||||
passesAllFilters = false
|
||||
}
|
||||
}
|
||||
if !passesAllFilters { // Not going with this processor further
|
||||
return nil, nil
|
||||
}
|
||||
smgEv, err := ccr.AsSMGenericEvent(reqProcessor.ContentFields)
|
||||
smgEv, err := ccr.AsSMGenericEvent(reqProcessor.CCRFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -98,8 +98,8 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro
|
||||
cca := NewCCAFromCCR(ccr)
|
||||
cca.OriginHost = self.cgrCfg.DiameterAgentCfg().OriginHost
|
||||
cca.OriginRealm = self.cgrCfg.DiameterAgentCfg().OriginRealm
|
||||
cca.GrantedServiceUnit.CCTime = int(maxUsage)
|
||||
cca.ResultCode = diam.Success
|
||||
cca.GrantedServiceUnit.CCTime = int(maxUsage)
|
||||
return cca, nil
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) {
|
||||
utils.Logger.Err(fmt.Sprintf("<DiameterAgent> No request processor enabled for CCR: %+v, ignoring request", ccr))
|
||||
return
|
||||
}
|
||||
if dmtA, err := cca.AsDiameterMessage(); err != nil {
|
||||
if dmtA, err := cca.AsBareDiameterMessage(); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Failed to convert cca as diameter message, error: %s", err.Error()))
|
||||
return
|
||||
} else if _, err := dmtA.WriteTo(c); err != nil {
|
||||
|
||||
@@ -139,11 +139,11 @@ func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) {
|
||||
if ccr.diamMessage, err = ccr.AsDiameterMessage(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
eSMGE := sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "AccId": "routinga;1442095190;1476802709",
|
||||
eSMGE := sessionmanager.SMGenericEvent{"EventName": DIAMETER_CCR, "AccId": "routinga;1442095190;1476802709",
|
||||
"Account": "*users", "AnswerTime": "2015-11-23 12:22:24 +0000 UTC", "Category": "call",
|
||||
"Destination": "4986517174964", "Direction": "*out", "ReqType": "*users", "SetupTime": "2015-11-23 12:22:24 +0000 UTC",
|
||||
"Subject": "*users", "SubscriberId": "4986517174963", "TOR": "*voice", "Tenant": "*users", "Usage": "300"}
|
||||
if smge, err := ccr.AsSMGenericEvent(cfgDefaults.DiameterAgentCfg().RequestProcessors[0].ContentFields); err != nil {
|
||||
if smge, err := ccr.AsSMGenericEvent(cfgDefaults.DiameterAgentCfg().RequestProcessors[0].CCRFields); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eSMGE, smge) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eSMGE, smge)
|
||||
|
||||
287
agents/libdmt.go
287
agents/libdmt.go
@@ -23,6 +23,7 @@ Build various type of packets here
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
@@ -47,9 +48,8 @@ func init() {
|
||||
}
|
||||
|
||||
const (
|
||||
META_CCR_USAGE = "*ccr_usage"
|
||||
META_CCR_SMG_EVENT_NAME = "*ccr_smg_event_name"
|
||||
DIAMETER_CCR = "DIAMETER_CCR"
|
||||
META_CCR_USAGE = "*ccr_usage"
|
||||
DIAMETER_CCR = "DIAMETER_CCR"
|
||||
)
|
||||
|
||||
func loadDictionaries(dictsDir, componentId string) error {
|
||||
@@ -155,6 +155,148 @@ func storedCdrToCCR(cdr *engine.StoredCdr, originHost, originRealm string, vendo
|
||||
return ccr
|
||||
}
|
||||
|
||||
// Not the cleanest but most efficient way to retrieve a string from AVP since there are string methods on all datatypes
|
||||
// and the output is always in teh form "DataType{real_string}Padding:x"
|
||||
func avpValAsString(a *diam.AVP) string {
|
||||
dataVal := a.Data.String()
|
||||
startIdx := strings.Index(dataVal, "{")
|
||||
endIdx := strings.Index(dataVal, "}")
|
||||
if startIdx == 0 || endIdx == 0 {
|
||||
return ""
|
||||
}
|
||||
return dataVal[startIdx+1 : endIdx]
|
||||
}
|
||||
|
||||
// Handler for meta functions
|
||||
func metaHandler(m *diam.Message, tag, arg string, debitInterval time.Duration) (string, error) {
|
||||
switch tag {
|
||||
case META_CCR_USAGE:
|
||||
ccReqTypeAvp, err := m.FindAVP("CC-Request-Type", dict.UndefinedVendorID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if ccReqTypeAvp == nil {
|
||||
return "", errors.New("CC-Request-Type not found")
|
||||
}
|
||||
ccReqNrAvp, err := m.FindAVP("CC-Request-Number", dict.UndefinedVendorID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if ccReqNrAvp == nil {
|
||||
return "", errors.New("CC-Request-Number not found")
|
||||
}
|
||||
reqUnitAVPs, err := m.FindAVPsWithPath([]interface{}{"Requested-Service-Unit", "CC-Time"}, dict.UndefinedVendorID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if len(reqUnitAVPs) == 0 {
|
||||
return "", errors.New("Requested-Service-Unit/CC-Time not found")
|
||||
}
|
||||
usedUnitAVPs, err := m.FindAVPsWithPath([]interface{}{"Used-Service-Unit", "CC-Time"}, dict.UndefinedVendorID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if len(usedUnitAVPs) == 0 {
|
||||
return "", errors.New("Used-Service-Unit/CC-Time not found")
|
||||
}
|
||||
usage := usageFromCCR(int(ccReqTypeAvp.Data.(datatype.Enumerated)),
|
||||
int(ccReqNrAvp.Data.(datatype.Enumerated)),
|
||||
int(reqUnitAVPs[0].Data.(datatype.Unsigned32)),
|
||||
int(usedUnitAVPs[0].Data.(datatype.Unsigned32)), debitInterval)
|
||||
return strconv.FormatFloat(usage.Seconds(), 'f', -1, 64), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func avpsWithPath(m *diam.Message, rsrFld *utils.RSRField) ([]*diam.AVP, error) {
|
||||
hierarchyPath := strings.Split(rsrFld.Id, utils.HIERARCHY_SEP)
|
||||
hpIf := make([]interface{}, len(hierarchyPath))
|
||||
for i, val := range hierarchyPath {
|
||||
hpIf[i] = val
|
||||
}
|
||||
return m.FindAVPsWithPath(hpIf, dict.UndefinedVendorID)
|
||||
}
|
||||
|
||||
// Follows the implementation in the StorCdr
|
||||
func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField) (bool, int) {
|
||||
if fieldFilter == nil {
|
||||
return true, 0
|
||||
}
|
||||
avps, err := avpsWithPath(m, fieldFilter)
|
||||
if err != nil {
|
||||
return false, 0
|
||||
} else if len(avps) == 0 {
|
||||
return true, 0
|
||||
}
|
||||
for avpIdx, avpVal := range avps {
|
||||
if fieldFilter.FilterPasses(avpValAsString(avpVal)) {
|
||||
return true, avpIdx
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int) string {
|
||||
var outVal string
|
||||
for _, rsrTpl := range outTpl {
|
||||
if rsrTpl.IsStatic() {
|
||||
outVal += rsrTpl.ParseValue("")
|
||||
} else {
|
||||
matchingAvps, err := avpsWithPath(m, rsrTpl)
|
||||
if err != nil || len(matchingAvps) == 0 {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id))
|
||||
continue // Filter not matching
|
||||
}
|
||||
if len(matchingAvps) <= avpIdx {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot retrieve AVP with index %d for field template with id: %s", avpIdx, rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
if matchingAvps[0].Data.Type() == diam.GroupedAVPType {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Value for field template with id: %s is matching a group AVP, ignoring.", rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
outVal += avpValAsString(matchingAvps[avpIdx])
|
||||
}
|
||||
}
|
||||
return outVal
|
||||
}
|
||||
|
||||
func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, debitInterval time.Duration) (fmtValOut string, err error) {
|
||||
var outVal string
|
||||
switch cfgFld.Type {
|
||||
case utils.META_FILLER:
|
||||
outVal = cfgFld.Value.Id()
|
||||
cfgFld.Padding = "right"
|
||||
case utils.META_CONSTANT:
|
||||
outVal = cfgFld.Value.Id()
|
||||
case utils.META_HANDLER:
|
||||
outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, debitInterval)
|
||||
if err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error()))
|
||||
}
|
||||
case utils.META_COMPOSED:
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, 0)
|
||||
case utils.MetaGrouped: // GroupedAVP
|
||||
passAtIndex := -1
|
||||
matchedAllFilters := true
|
||||
for _, fldFilter := range cfgFld.FieldFilter {
|
||||
var pass bool
|
||||
if pass, passAtIndex = passesFieldFilter(m, fldFilter); !pass {
|
||||
matchedAllFilters = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchedAllFilters {
|
||||
return "", nil // Not matching field filters, will have it empty
|
||||
}
|
||||
if passAtIndex == -1 {
|
||||
passAtIndex = 0 // No filter
|
||||
}
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex)
|
||||
}
|
||||
if fmtValOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Error when processing field template with tag: %s, error: %s", cfgFld.Tag, err.Error()))
|
||||
return "", err
|
||||
}
|
||||
return fmtValOut, nil
|
||||
}
|
||||
|
||||
// debitInterval is the configured debitInterval, in sync with the diameter client one
|
||||
func NewCCRFromDiameterMessage(m *diam.Message, debitInterval time.Duration) (*CCR, error) {
|
||||
var ccr CCR
|
||||
@@ -288,127 +430,12 @@ func (self *CCR) AsDiameterMessage() (*diam.Message, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Not the cleanest but most efficient way to retrieve a string from AVP since there are string methods on all datatypes
|
||||
// and the output is always in teh form "DataType{real_string}Padding:x"
|
||||
func avpValAsString(a *diam.AVP) string {
|
||||
dataVal := a.Data.String()
|
||||
startIdx := strings.Index(dataVal, "{")
|
||||
endIdx := strings.Index(dataVal, "}")
|
||||
if startIdx == 0 || endIdx == 0 {
|
||||
return ""
|
||||
}
|
||||
return dataVal[startIdx+1 : endIdx]
|
||||
}
|
||||
|
||||
// Handler for meta functions
|
||||
func (self *CCR) metaHandler(tag, arg string) (string, error) {
|
||||
switch tag {
|
||||
case META_CCR_USAGE:
|
||||
usage := usageFromCCR(self.CCRequestType, self.CCRequestNumber, self.RequestedServiceUnit.CCTime, self.UsedServiceUnit.CCTime, self.debitInterval)
|
||||
return strconv.FormatFloat(usage.Seconds(), 'f', -1, 64), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (self *CCR) avpsWithPath(rsrFld *utils.RSRField) ([]*diam.AVP, error) {
|
||||
hierarchyPath := strings.Split(rsrFld.Id, utils.HIERARCHY_SEP)
|
||||
hpIf := make([]interface{}, len(hierarchyPath))
|
||||
for i, val := range hierarchyPath {
|
||||
hpIf[i] = val
|
||||
}
|
||||
return self.diamMessage.FindAVPsWithPath(hpIf, dict.UndefinedVendorID)
|
||||
}
|
||||
|
||||
// Follows the implementation in the StorCdr
|
||||
func (self *CCR) passesFieldFilter(fieldFilter *utils.RSRField) (bool, int) {
|
||||
if fieldFilter == nil {
|
||||
return true, 0
|
||||
}
|
||||
avps, err := self.avpsWithPath(fieldFilter)
|
||||
if err != nil {
|
||||
return false, 0
|
||||
} else if len(avps) == 0 {
|
||||
return true, 0
|
||||
}
|
||||
for avpIdx, avpVal := range avps {
|
||||
if fieldFilter.FilterPasses(avpValAsString(avpVal)) {
|
||||
return true, avpIdx
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (self *CCR) eventFieldValue(fldTpl utils.RSRFields, avpIdx int) string {
|
||||
var outVal string
|
||||
for _, rsrTpl := range fldTpl {
|
||||
if rsrTpl.IsStatic() {
|
||||
outVal += rsrTpl.ParseValue("")
|
||||
} else {
|
||||
matchingAvps, err := self.avpsWithPath(rsrTpl)
|
||||
if err != nil || len(matchingAvps) == 0 {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id))
|
||||
continue // Filter not matching
|
||||
}
|
||||
if len(matchingAvps) <= avpIdx {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot retrieve AVP with index %d for field template with id: %s", avpIdx, rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
if matchingAvps[0].Data.Type() == diam.GroupedAVPType {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Value for field template with id: %s is matching a group AVP, ignoring.", rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
outVal += avpValAsString(matchingAvps[avpIdx])
|
||||
}
|
||||
}
|
||||
return outVal
|
||||
}
|
||||
|
||||
func (self *CCR) fieldOutVal(cfgFld *config.CfgCdrField) (fmtValOut string, err error) {
|
||||
var outVal string
|
||||
switch cfgFld.Type {
|
||||
case utils.META_FILLER:
|
||||
outVal = cfgFld.Value.Id()
|
||||
cfgFld.Padding = "right"
|
||||
case utils.META_CONSTANT:
|
||||
outVal = cfgFld.Value.Id()
|
||||
case utils.META_HANDLER:
|
||||
outVal, err = self.metaHandler(cfgFld.HandlerId, cfgFld.Layout)
|
||||
if err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error()))
|
||||
}
|
||||
case utils.META_COMPOSED:
|
||||
outVal = self.eventFieldValue(cfgFld.Value, 0)
|
||||
case utils.MetaGrouped: // GroupedAVP
|
||||
passAtIndex := -1
|
||||
matchedAllFilters := true
|
||||
for _, fldFilter := range cfgFld.FieldFilter {
|
||||
var pass bool
|
||||
if pass, passAtIndex = self.passesFieldFilter(fldFilter); !pass {
|
||||
matchedAllFilters = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchedAllFilters {
|
||||
return "", nil // Not matching field filters, will have it empty
|
||||
}
|
||||
if passAtIndex == -1 {
|
||||
passAtIndex = 0 // No filter
|
||||
}
|
||||
outVal = self.eventFieldValue(cfgFld.Value, passAtIndex)
|
||||
}
|
||||
if fmtValOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Error when processing field template with tag: %s, error: %s", cfgFld.Tag, err.Error()))
|
||||
return "", err
|
||||
}
|
||||
return fmtValOut, nil
|
||||
}
|
||||
|
||||
// Extracts data out of CCR into a SMGenericEvent based on the configured template
|
||||
func (self *CCR) AsSMGenericEvent(cfgFlds []*config.CfgCdrField) (sessionmanager.SMGenericEvent, error) {
|
||||
outMap := make(map[string]string) // work with it so we can append values to keys
|
||||
outMap[utils.EVENT_NAME] = DIAMETER_CCR
|
||||
for _, cfgFld := range cfgFlds {
|
||||
fmtOut, err := self.fieldOutVal(cfgFld)
|
||||
fmtOut, err := fieldOutVal(self.diamMessage, cfgFld, self.debitInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -444,7 +471,7 @@ type CCA struct {
|
||||
}
|
||||
|
||||
// Converts itself into DiameterMessage
|
||||
func (self *CCA) AsDiameterMessage() (*diam.Message, error) {
|
||||
func (self *CCA) AsBareDiameterMessage() (*diam.Message, error) {
|
||||
if _, err := self.diamMessage.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String(self.SessionId)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -466,14 +493,16 @@ func (self *CCA) AsDiameterMessage() (*diam.Message, error) {
|
||||
if _, err := self.diamMessage.NewAVP(avp.ResultCode, avp.Mbit, 0, datatype.Unsigned32(self.ResultCode)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ccTimeAvp, err := self.diamMessage.Dictionary().FindAVP(self.diamMessage.Header.ApplicationID, "CC-Time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := self.diamMessage.NewAVP("Granted-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(ccTimeAvp.Code, avp.Mbit, 0, datatype.Unsigned32(self.GrantedServiceUnit.CCTime))}}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
/*
|
||||
ccTimeAvp, err := self.diamMessage.Dictionary().FindAVP(self.diamMessage.Header.ApplicationID, "CC-Time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := self.diamMessage.NewAVP("Granted-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(ccTimeAvp.Code, avp.Mbit, 0, datatype.Unsigned32(self.GrantedServiceUnit.CCTime))}}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
return self.diamMessage, nil
|
||||
}
|
||||
|
||||
@@ -99,11 +99,10 @@ func TestFieldOutVal(t *testing.T) {
|
||||
m.NewAVP("Requested-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(360))}}) // CC-Time
|
||||
ccr := &CCR{diamMessage: m}
|
||||
cfgFld := &config.CfgCdrField{Tag: "StaticTest", Type: utils.META_COMPOSED, FieldId: utils.TOR,
|
||||
Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut := "*voice"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
@@ -111,7 +110,7 @@ func TestFieldOutVal(t *testing.T) {
|
||||
cfgFld = &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION,
|
||||
Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "360"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
@@ -120,7 +119,7 @@ func TestFieldOutVal(t *testing.T) {
|
||||
cfgFld = &config.CfgCdrField{Tag: "Grouped1", Type: utils.MetaGrouped, FieldId: "Account",
|
||||
Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "33708000003"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
@@ -130,7 +129,7 @@ func TestFieldOutVal(t *testing.T) {
|
||||
FieldFilter: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type(1)", utils.INFIELD_SEP),
|
||||
Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "208708000003"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
|
||||
@@ -277,7 +277,7 @@ const CGRATES_CFG_JSON = `
|
||||
"dry_run": false, // do not send the CDRs to CDRS, just parse them
|
||||
"request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor
|
||||
"continue_on_success": false, // continue to the next template if executed
|
||||
"content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
|
||||
"ccr_fields":[ // fields taken out of CCR and used in internal requests
|
||||
{"tag": "tor", "field_id": "TOR", "type": "*composed", "value": "^*voice", "mandatory": true},
|
||||
{"tag": "accid", "field_id": "AccId", "type": "*composed", "value": "Session-Id", "mandatory": true},
|
||||
{"tag": "reqtype", "field_id": "ReqType", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
@@ -292,6 +292,9 @@ const CGRATES_CFG_JSON = `
|
||||
{"tag": "usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true},
|
||||
{"tag": "subscriber_id", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
|
||||
],
|
||||
"cca_fields":[ // fields returned in CCA
|
||||
//{"tag": "tor", "field_id": "TOR", "type": "*composed", "value": "^*voice", "mandatory": true},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -433,7 +433,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) {
|
||||
Dry_run: utils.BoolPointer(false),
|
||||
Request_filter: utils.StringPointer("Subscription-Id>Subscription-Id-Type(0)"),
|
||||
Continue_on_success: utils.BoolPointer(false),
|
||||
Content_fields: &[]*CdrFieldJsonCfg{
|
||||
CCR_fields: &[]*CdrFieldJsonCfg{
|
||||
&CdrFieldJsonCfg{Tag: utils.StringPointer("tor"), Field_id: utils.StringPointer(utils.TOR), Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("^*voice"), Mandatory: utils.BoolPointer(true)},
|
||||
&CdrFieldJsonCfg{Tag: utils.StringPointer("accid"), Field_id: utils.StringPointer(utils.ACCID), Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
@@ -461,13 +461,15 @@ func TestDiameterAgentJsonCfg(t *testing.T) {
|
||||
&CdrFieldJsonCfg{Tag: utils.StringPointer("subscriber_id"), Field_id: utils.StringPointer("SubscriberId"), Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("Subscription-Id>Subscription-Id-Data"), Mandatory: utils.BoolPointer(true)},
|
||||
},
|
||||
CCA_fields: &[]*CdrFieldJsonCfg{},
|
||||
},
|
||||
},
|
||||
}
|
||||
if cfg, err := dfCgrJsonCfg.DiameterAgentJsonCfg(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eCfg, cfg) {
|
||||
t.Error("Received: ", cfg)
|
||||
rcv := *cfg.Request_processors
|
||||
t.Errorf("Received: %+v", rcv[0].CCA_fields)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,8 @@ type DARequestProcessor struct {
|
||||
DryRun bool
|
||||
RequestFilter utils.RSRFields
|
||||
ContinueOnSuccess bool
|
||||
ContentFields []*CfgCdrField
|
||||
CCRFields []*CfgCdrField
|
||||
CCAFields []*CfgCdrField
|
||||
}
|
||||
|
||||
func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg) error {
|
||||
@@ -119,8 +120,13 @@ func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg
|
||||
return err
|
||||
}
|
||||
}
|
||||
if jsnCfg.Content_fields != nil {
|
||||
if self.ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Content_fields); err != nil {
|
||||
if jsnCfg.CCR_fields != nil {
|
||||
if self.CCRFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.CCR_fields); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if jsnCfg.CCA_fields != nil {
|
||||
if self.CCAFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.CCA_fields); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,8 @@ type DARequestProcessorJsnCfg struct {
|
||||
Dry_run *bool
|
||||
Request_filter *string
|
||||
Continue_on_success *bool
|
||||
Content_fields *[]*CdrFieldJsonCfg
|
||||
CCR_fields *[]*CdrFieldJsonCfg
|
||||
CCA_fields *[]*CdrFieldJsonCfg
|
||||
}
|
||||
|
||||
// History server config section
|
||||
|
||||
Reference in New Issue
Block a user