Add XML to EventReader and test for it

This commit is contained in:
TeoV
2020-01-11 14:52:37 +02:00
parent 8ed5fc3956
commit 4ea9cd3a48
18 changed files with 1126 additions and 223 deletions

View File

@@ -23,8 +23,6 @@ import (
"errors"
"fmt"
"io"
"net"
"strconv"
"strings"
"time"
@@ -34,17 +32,6 @@ import (
"github.com/cgrates/cgrates/utils"
)
// getElementText will process the node to extract the elementName's text out of it (only first one found)
// returns utils.ErrNotFound if the element is not found in the node
func elementText(xmlElement *xmlquery.Node, elmntPath string) (string, error) {
elmnt := xmlquery.FindOne(xmlElement, elmntPath)
if elmnt == nil {
return "", utils.ErrNotFound
}
return elmnt.InnerText(), nil
}
// handlerUsageDiff will calculate the usage as difference between timeEnd and timeStart
// Expects the 2 arguments in template separated by |
func handlerSubstractUsage(xmlElement *xmlquery.Node, argsTpl config.RSRParsers,
@@ -57,7 +44,7 @@ func handlerSubstractUsage(xmlElement *xmlquery.Node, argsTpl config.RSRParsers,
}
absolutePath := utils.ParseHierarchyPath(rsrArg.Rules, "")
relPath := utils.HierarchyPath(absolutePath[len(cdrPath)+1:]) // Need relative path to the xmlElmnt
argStr, _ := elementText(xmlElement, relPath.AsString("/", false))
argStr, _ := config.ElementText(xmlElement, relPath.AsString("/", false))
argsStr += argStr
}
handlerArgs := strings.Split(argsStr, utils.HandlerArgSep)
@@ -116,7 +103,7 @@ func (xmlProc *XMLRecordsProcessor) ProcessNextRecord() (cdrs []*engine.CDR, err
cdrs = make([]*engine.CDR, 0)
cdrXML := xmlProc.cdrXmlElmts[xmlProc.procItems]
xmlProc.procItems += 1
xmlProvider := newXmlProvider(cdrXML, xmlProc.cdrPath)
xmlProvider := config.NewXmlProvider(cdrXML, xmlProc.cdrPath, utils.MetaReq)
for _, cdrcCfg := range xmlProc.cdrcCfgs {
tenant, err := cdrcCfg.Tenant.ParseDataProvider(xmlProvider, utils.NestingSep)
if err != nil {
@@ -148,7 +135,7 @@ func (xmlProc *XMLRecordsProcessor) recordToCDR(xmlEntity *xmlquery.Node, cdrcCf
var lazyHttpFields []*config.FCTemplate
var err error
fldVals := make(map[string]string)
xmlProvider := newXmlProvider(xmlEntity, xmlProc.cdrPath)
xmlProvider := config.NewXmlProvider(xmlEntity, xmlProc.cdrPath, utils.MetaReq)
for _, cdrFldCfg := range cdrcCfg.ContentFields {
if len(cdrFldCfg.Filters) != 0 {
if pass, err := xmlProc.filterS.Pass(tenant,
@@ -207,80 +194,3 @@ func (xmlProc *XMLRecordsProcessor) recordToCDR(xmlEntity *xmlquery.Node, cdrcCf
}
return cdr, nil
}
// newXmlProvider constructs a DataProvider
func newXmlProvider(req *xmlquery.Node, cdrPath utils.HierarchyPath) (dP config.DataProvider) {
dP = &xmlProvider{req: req, cdrPath: cdrPath, cache: config.NewNavigableMap(nil)}
return
}
// xmlProvider implements engine.DataProvider so we can pass it to filters
type xmlProvider struct {
req *xmlquery.Node
cdrPath utils.HierarchyPath //used to compute relative path
cache *config.NavigableMap
}
// String is part of engine.DataProvider interface
// when called, it will display the already parsed values out of cache
func (xP *xmlProvider) String() string {
return utils.ToJSON(xP)
}
// FieldAsInterface is part of engine.DataProvider interface
func (xP *xmlProvider) FieldAsInterface(fldPath []string) (data interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
if fldPath[0] != utils.MetaReq {
return "", utils.ErrPrefixNotFound(strings.Join(fldPath, utils.NestingSep))
}
if data, err = xP.cache.FieldAsInterface(fldPath); err == nil ||
err != utils.ErrNotFound { // item found in cache
return
}
err = nil // cancel previous err
relPath := utils.HierarchyPath(fldPath[len(xP.cdrPath)+1:]) // Need relative path to the xmlElmnt
var slctrStr string
for i := range relPath {
if sIdx := strings.Index(relPath[i], "["); sIdx != -1 {
slctrStr = relPath[i][sIdx:]
if slctrStr[len(slctrStr)-1:] != "]" {
return nil, fmt.Errorf("filter rule <%s> needs to end in ]", slctrStr)
}
relPath[i] = relPath[i][:sIdx]
if slctrStr[1:2] != "@" {
i, err := strconv.Atoi(slctrStr[1 : len(slctrStr)-1])
if err != nil {
return nil, err
}
slctrStr = "[" + strconv.Itoa(i+1) + "]"
}
relPath[i] = relPath[i] + slctrStr
}
}
data, err = elementText(xP.req, relPath.AsString("/", false))
xP.cache.Set(fldPath, data, false, false)
return
}
// FieldAsString is part of engine.DataProvider interface
func (xP *xmlProvider) FieldAsString(fldPath []string) (data string, err error) {
var valIface interface{}
valIface, err = xP.FieldAsInterface(fldPath)
if err != nil {
return
}
return utils.IfaceAsString(valIface), nil
}
// AsNavigableMap is part of engine.DataProvider interface
func (xP *xmlProvider) AsNavigableMap([]*config.FCTemplate) (
nm *config.NavigableMap, err error) {
return nil, utils.ErrNotImplemented
}
// RemoteHost is part of engine.DataProvider interface
func (xP *xmlProvider) RemoteHost() net.Addr {
return utils.LocalAddr()
}

View File

@@ -132,7 +132,6 @@ func TestXmlITAnalyseCDRs(t *testing.T) {
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
func TestXmlITKillEngine(t *testing.T) {

View File

@@ -166,28 +166,40 @@ var cdrXmlBroadsoft = `<?xml version="1.0" encoding="ISO-8859-1"?>
</cdrData>
</broadWorksCDR>`
func TestXMLElementText(t *testing.T) {
doc, err := xmlquery.Parse(strings.NewReader(cdrXmlBroadsoft))
if err != nil {
t.Error(err)
}
cdrs := xmlquery.Find(doc, path.Join("/broadWorksCDR/cdrData/"))
cdrWithoutUserNr := cdrs[0]
if _, err := elementText(cdrWithoutUserNr, "basicModule/userNumber"); err != utils.ErrNotFound {
t.Error(err)
}
cdrWithUser := cdrs[1]
if val, err := elementText(cdrWithUser, "basicModule/userNumber"); err != nil {
t.Error(err)
} else if val != "1001" {
t.Errorf("Expecting: 1001, received: %s", val)
}
if val, err := elementText(cdrWithUser, "centrexModule/locationList/locationInformation/locationType"); err != nil {
t.Error(err)
} else if val != "Primary Device" {
t.Errorf("Expecting: <Primary Device>, received: <%s>", val)
}
}
var xmlMultipleIndex = `<complete-success-notification callid="109870">
<createtime>2005-08-26T14:16:42</createtime>
<connecttime>2005-08-26T14:16:56</connecttime>
<endtime>2005-08-26T14:17:34</endtime>
<reference>My Call Reference</reference>
<userid>386</userid>
<username>sampleusername</username>
<customerid>1</customerid>
<companyname>Conecto LLC</companyname>
<totalcost amount="0.21" currency="USD">US$0.21</totalcost>
<hasrecording>yes</hasrecording>
<hasvoicemail>no</hasvoicemail>
<agenttotalcost amount="0.13" currency="USD">US$0.13</agenttotalcost>
<agentid>44</agentid>
<callleg calllegid="222146">
<number>+441624828505</number>
<description>Isle of Man</description>
<seconds>38</seconds>
<perminuterate amount="0.0200" currency="USD">US$0.0200</perminuterate>
<cost amount="0.0140" currency="USD">US$0.0140</cost>
<agentperminuterate amount="0.0130" currency="USD">US$0.0130</agentperminuterate>
<agentcost amount="0.0082" currency="USD">US$0.0082</agentcost>
</callleg>
<callleg calllegid="222147">
<number>+44 7624 494075</number>
<description>Isle of Man</description>
<seconds>37</seconds>
<perminuterate amount="0.2700" currency="USD">US$0.2700</perminuterate>
<cost amount="0.1890" currency="USD">US$0.1890</cost>
<agentperminuterate amount="0.1880" currency="USD">US$0.1880</agentperminuterate>
<agentcost amount="0.1159" currency="USD">US$0.1159</agentcost>
</callleg>
</complete-success-notification>
`
func TestXMLHandlerSubstractUsage(t *testing.T) {
doc, err := xmlquery.Parse(strings.NewReader(cdrXmlBroadsoft))
@@ -525,28 +537,6 @@ var xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
</File>
`
func TestXMLElementText3(t *testing.T) {
doc, err := xmlquery.Parse(strings.NewReader(xmlContent))
if err != nil {
t.Error(err)
}
hPath2 := utils.ParseHierarchyPath("File.CDRs.Call", "")
cdrs := xmlquery.Find(doc, hPath2.AsString("/", true))
if len(cdrs) != 3 {
t.Errorf("Expecting: 3, received: %+v", len(cdrs))
}
if _, err := elementText(cdrs[0], "SignalingInfo/PChargingVector/test"); err != utils.ErrNotFound {
t.Error(err)
}
if val, err := elementText(cdrs[1], "SignalingInfo/PChargingVector/icidvalue"); err != nil {
t.Error(err)
} else if val != "46d7974398c2671016afccc3f2c428c7" {
t.Errorf("Expecting: 46d7974398c2671016afccc3f2c428c7, received: %s", val)
}
}
func TestXMLRPNestingSeparator(t *testing.T) {
cdrcCfgs := []*config.CdrcCfg{
{
@@ -627,71 +617,3 @@ func TestXMLRPNestingSeparator(t *testing.T) {
t.Errorf("Expecting: %+v\n, received: %+v\n", expectedCDRs, cdrs)
}
}
var xmlMultipleIndex = `<complete-success-notification callid="109870">
<createtime>2005-08-26T14:16:42</createtime>
<connecttime>2005-08-26T14:16:56</connecttime>
<endtime>2005-08-26T14:17:34</endtime>
<reference>My Call Reference</reference>
<userid>386</userid>
<username>sampleusername</username>
<customerid>1</customerid>
<companyname>Conecto LLC</companyname>
<totalcost amount="0.21" currency="USD">US$0.21</totalcost>
<hasrecording>yes</hasrecording>
<hasvoicemail>no</hasvoicemail>
<agenttotalcost amount="0.13" currency="USD">US$0.13</agenttotalcost>
<agentid>44</agentid>
<callleg calllegid="222146">
<number>+441624828505</number>
<description>Isle of Man</description>
<seconds>38</seconds>
<perminuterate amount="0.0200" currency="USD">US$0.0200</perminuterate>
<cost amount="0.0140" currency="USD">US$0.0140</cost>
<agentperminuterate amount="0.0130" currency="USD">US$0.0130</agentperminuterate>
<agentcost amount="0.0082" currency="USD">US$0.0082</agentcost>
</callleg>
<callleg calllegid="222147">
<number>+44 7624 494075</number>
<description>Isle of Man</description>
<seconds>37</seconds>
<perminuterate amount="0.2700" currency="USD">US$0.2700</perminuterate>
<cost amount="0.1890" currency="USD">US$0.1890</cost>
<agentperminuterate amount="0.1880" currency="USD">US$0.1880</agentperminuterate>
<agentcost amount="0.1159" currency="USD">US$0.1159</agentcost>
</callleg>
</complete-success-notification>
`
func TestXMLIndexes(t *testing.T) {
doc, err := xmlquery.Parse(strings.NewReader(xmlMultipleIndex))
if err != nil {
t.Error(err)
}
dP := newXmlProvider(doc, utils.HierarchyPath([]string{}))
if data, err := dP.FieldAsString([]string{"*req", "complete-success-notification", "userid"}); err != nil {
t.Error(err)
} else if data != "386" {
t.Errorf("expecting: 386, received: <%s>", data)
}
if data, err := dP.FieldAsString([]string{"*req", "complete-success-notification", "username"}); err != nil {
t.Error(err)
} else if data != "sampleusername" {
t.Errorf("expecting: sampleusername, received: <%s>", data)
}
if data, err := dP.FieldAsString([]string{"*req", "complete-success-notification", "callleg", "seconds"}); err != nil {
t.Error(err)
} else if data != "38" {
t.Errorf("expecting: 38, received: <%s>", data)
}
if data, err := dP.FieldAsString([]string{"*req", "complete-success-notification", "callleg[1]", "seconds"}); err != nil {
t.Error(err)
} else if data != "37" {
t.Errorf("expecting: 37, received: <%s>", data)
}
if data, err := dP.FieldAsString([]string{"*req", "complete-success-notification", "callleg[@calllegid='222147']", "seconds"}); err != nil {
t.Error(err)
} else if data != "37" {
t.Errorf("expecting: 37, received: <%s>", data)
}
}