mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-17 14:19:54 +05:00
Move config/rsrparser.go to utils package
This commit is contained in:
committed by
Dan Christian Bogos
parent
36a7d174ab
commit
b1a5874215
440
utils/rsrparser.go
Normal file
440
utils/rsrparser.go
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
spltRgxp = regexp.MustCompile(`:s\/`)
|
||||
rulesRgxp = regexp.MustCompile(`(?:(.*[^\\])\/(.*[^\\])*\/){1,}`)
|
||||
)
|
||||
|
||||
// NewRSRParsers creates a new RSRParsers by spliting the rule using the separator
|
||||
func NewRSRParsers(parsersRules, sep string) (prsrs RSRParsers, err error) {
|
||||
if parsersRules == EmptyString {
|
||||
return
|
||||
}
|
||||
if count := strings.Count(parsersRules, RSRConstSep); count%2 != 0 { // check if we have matching `
|
||||
return nil, fmt.Errorf("Closed unspilit syntax")
|
||||
} else if count != 0 {
|
||||
var splitedRule []string
|
||||
for idx := strings.IndexByte(parsersRules, RSRConstChar); idx != -1; idx = strings.IndexByte(parsersRules, RSRConstChar) {
|
||||
insideARulePrefix := !strings.HasSuffix(parsersRules[:idx], InfieldSep) // if doesn't have ; we need to concatenate it with last rule
|
||||
if insideARulePrefix {
|
||||
splitedRule = append(splitedRule, strings.Split(parsersRules[:idx], InfieldSep)...)
|
||||
} else {
|
||||
splitedRule = append(splitedRule, strings.Split(parsersRules[:idx-1], InfieldSep)...)
|
||||
}
|
||||
parsersRules = parsersRules[idx+1:]
|
||||
idx = strings.IndexByte(parsersRules, RSRConstChar)
|
||||
if insideARulePrefix {
|
||||
splitedRule[len(splitedRule)-1] += parsersRules[:idx]
|
||||
} else {
|
||||
splitedRule = append(splitedRule, parsersRules[:idx])
|
||||
}
|
||||
parsersRules = parsersRules[idx+1:]
|
||||
count -= 2 // the number of ` remaining
|
||||
if len(parsersRules) == 0 {
|
||||
continue
|
||||
}
|
||||
insideARuleSufix := !strings.HasPrefix(parsersRules, InfieldSep) // if doesn't have ; we need to concatenate it with last rule
|
||||
if insideARuleSufix {
|
||||
idx = strings.IndexByte(parsersRules, FallbackSep) // ';'
|
||||
if idx == -1 {
|
||||
idx = len(parsersRules)
|
||||
splitedRule[len(splitedRule)-1] += parsersRules[:idx]
|
||||
break
|
||||
}
|
||||
splitedRule[len(splitedRule)-1] += parsersRules[:idx]
|
||||
} else {
|
||||
idx = 0
|
||||
}
|
||||
parsersRules = parsersRules[idx+1:]
|
||||
if len(parsersRules) == 0 {
|
||||
break
|
||||
}
|
||||
if count == 0 { // no more ` so add the rest
|
||||
splitedRule = append(splitedRule, strings.Split(parsersRules, InfieldSep)...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return NewRSRParsersFromSlice(splitedRule)
|
||||
}
|
||||
return NewRSRParsersFromSlice(strings.Split(parsersRules, sep))
|
||||
}
|
||||
|
||||
// NewRSRParsersFromSlice creates a new RSRParsers from a slice
|
||||
func NewRSRParsersFromSlice(parsersRules []string) (prsrs RSRParsers, err error) {
|
||||
prsrs = make(RSRParsers, len(parsersRules))
|
||||
for i, rlStr := range parsersRules {
|
||||
if prsrs[i], err = NewRSRParser(rlStr); err != nil {
|
||||
return nil, err
|
||||
} else if prsrs[i] == nil {
|
||||
return nil, fmt.Errorf("empty RSRParser in rule: <%s>", rlStr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewRSRParsersMustCompile creates a new RSRParsers and panic if fails
|
||||
func NewRSRParsersMustCompile(parsersRules string, rsrSeparator string) (prsrs RSRParsers) {
|
||||
var err error
|
||||
if prsrs, err = NewRSRParsers(parsersRules, rsrSeparator); err != nil {
|
||||
panic(fmt.Sprintf("rule: <%s>, error: %s", parsersRules, err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RSRParsers is a set of RSRParser
|
||||
type RSRParsers []*RSRParser
|
||||
|
||||
// GetRule returns the original string from which the rules were composed
|
||||
func (prsrs RSRParsers) GetRule() (out string) {
|
||||
for _, prsr := range prsrs {
|
||||
out += RSRSep + prsr.Rules
|
||||
}
|
||||
if len(out) != 0 {
|
||||
out = out[1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Compile parses Rules string and repopulates other fields
|
||||
func (prsrs RSRParsers) Compile() (err error) {
|
||||
for _, prsr := range prsrs {
|
||||
if err = prsr.Compile(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseValue will parse the value out considering converters
|
||||
func (prsrs RSRParsers) ParseValue(value any) (out string, err error) {
|
||||
for _, prsr := range prsrs {
|
||||
var outPrsr string
|
||||
if outPrsr, err = prsr.ParseValue(value); err != nil {
|
||||
return EmptyString, err
|
||||
}
|
||||
out += outPrsr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseDataProvider will parse the dataprovider using DPDynamicString
|
||||
func (prsrs RSRParsers) ParseDataProvider(dP DataProvider) (out string, err error) {
|
||||
for _, prsr := range prsrs {
|
||||
var outPrsr string
|
||||
if outPrsr, err = prsr.ParseDataProvider(dP); err != nil {
|
||||
return EmptyString, err
|
||||
}
|
||||
out += outPrsr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseDataProviderWithInterfaces will parse the dataprovider using DPDynamicInterface
|
||||
func (prsrs RSRParsers) ParseDataProviderWithInterfaces(dP DataProvider) (out string, err error) {
|
||||
for _, prsr := range prsrs {
|
||||
var outPrsr string
|
||||
if outPrsr, err = prsr.ParseDataProviderWithInterfaces(dP); err != nil {
|
||||
return EmptyString, err
|
||||
}
|
||||
out += outPrsr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseDataProviderWithInterfaces will parse the dataprovider using DPDynamicInterface
|
||||
func (prsrs RSRParsers) ParseDataProviderWithInterfaces2(dP DataProvider) (out any, err error) {
|
||||
for i, prsr := range prsrs {
|
||||
outPrsr, err := prsr.ParseDataProviderWithInterfaces2(dP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i == 0 {
|
||||
out = outPrsr
|
||||
} else {
|
||||
out = IfaceAsString(out) + IfaceAsString(outPrsr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetIfaceFromValues returns an interface for each RSRParser
|
||||
func (prsrs RSRParsers) GetIfaceFromValues(evNm DataProvider) (iFaceVals []any, err error) {
|
||||
iFaceVals = make([]any, len(prsrs))
|
||||
for i, val := range prsrs {
|
||||
var strVal string
|
||||
if strVal, err = val.ParseDataProvider(evNm); err != nil {
|
||||
return
|
||||
}
|
||||
iFaceVals[i] = StringToInterface(strVal)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of RSRParsers
|
||||
func (prsrs RSRParsers) Clone() (cln RSRParsers) {
|
||||
if prsrs == nil {
|
||||
return nil
|
||||
}
|
||||
cln = make(RSRParsers, len(prsrs))
|
||||
for i, prsr := range prsrs {
|
||||
cln[i] = prsr.Clone()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (prsrs RSRParsers) AsStringSlice() (v []string) {
|
||||
v = make([]string, len(prsrs))
|
||||
for i, val := range prsrs {
|
||||
v[i] = val.Rules
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewRSRParser builds one RSRParser
|
||||
func NewRSRParser(parserRules string) (rsrParser *RSRParser, err error) {
|
||||
if len(parserRules) == 0 {
|
||||
return
|
||||
}
|
||||
rsrParser = &RSRParser{Rules: parserRules}
|
||||
if err = rsrParser.Compile(); err != nil {
|
||||
rsrParser = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewRSRParserMustCompile creates a new RSRParser and panic if fails
|
||||
func NewRSRParserMustCompile(parserRules string) (rsrPrsr *RSRParser) {
|
||||
var err error
|
||||
if rsrPrsr, err = NewRSRParser(parserRules); err != nil {
|
||||
panic(fmt.Sprintf("compiling rules: <%s>, error: %s", parserRules, err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RSRParser is a parser for data coming from various sources
|
||||
type RSRParser struct {
|
||||
Rules string // Rules container holding the string rules, public so it can be stored
|
||||
|
||||
path string // instruct extracting info out of header in event
|
||||
rsrRules []*ReSearchReplace // rules to use when parsing value
|
||||
converters DataConverters // set of converters to apply on output
|
||||
|
||||
dynIdxStart int
|
||||
dynIdxEnd int
|
||||
dynRules RSRParsers
|
||||
}
|
||||
|
||||
// AttrName exports the attribute name of the RSRParser
|
||||
func (prsr *RSRParser) AttrName() string {
|
||||
return strings.TrimPrefix(prsr.path, DynamicDataPrefix)
|
||||
}
|
||||
|
||||
// Compile parses Rules string and repopulates other fields
|
||||
func (prsr *RSRParser) Compile() (err error) {
|
||||
parserRules := prsr.Rules
|
||||
|
||||
if dynIdxStart := strings.IndexByte(parserRules, RSRDynStartChar); dynIdxStart != -1 {
|
||||
if dynIdxEnd := strings.IndexByte(parserRules[dynIdxStart:], RSRDynEndChar); dynIdxEnd != -1 {
|
||||
var dynrules RSRParsers
|
||||
if dynrules, err = NewRSRParsers(parserRules[dynIdxStart+1:dynIdxStart+dynIdxEnd],
|
||||
RSRSep); err != nil {
|
||||
return
|
||||
}
|
||||
prsr.dynRules = dynrules
|
||||
prsr.dynIdxStart = dynIdxStart
|
||||
prsr.dynIdxEnd = dynIdxStart + dynIdxEnd + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if idxConverters := strings.Index(parserRules, RSRDataConverterPrefix); idxConverters != -1 { // converters in the string
|
||||
if !strings.HasSuffix(parserRules, RSRDataConverterSufix) {
|
||||
return fmt.Errorf("invalid converter terminator in rule: <%s>",
|
||||
parserRules)
|
||||
}
|
||||
convertersStr := parserRules[idxConverters+1 : len(parserRules)-1] // strip also {}
|
||||
convsSplt := strings.Split(convertersStr, ANDSep)
|
||||
prsr.converters = make(DataConverters, len(convsSplt))
|
||||
for i, convStr := range convsSplt {
|
||||
var conv DataConverter
|
||||
if conv, err = NewDataConverter(convStr); err != nil {
|
||||
return fmt.Errorf("invalid converter value in string: <%s>, err: %s",
|
||||
convStr, err.Error())
|
||||
}
|
||||
prsr.converters[i] = conv
|
||||
}
|
||||
parserRules = parserRules[:idxConverters]
|
||||
}
|
||||
if !strings.HasPrefix(parserRules, DynamicDataPrefix) ||
|
||||
len(parserRules) == 1 { // special case when RSR is defined as static attribute
|
||||
prsr.path = parserRules
|
||||
return
|
||||
}
|
||||
// dynamic content via attributeNames
|
||||
spltRules := spltRgxp.Split(parserRules, -1)
|
||||
prsr.path = spltRules[0] // in form ~hdr_name
|
||||
prsr.rsrRules = make([]*ReSearchReplace, 0, len(spltRules[1:]))
|
||||
if len(spltRules) > 1 {
|
||||
for _, ruleStr := range spltRules[1:] { // :s/ already removed through split
|
||||
allMatches := rulesRgxp.FindStringSubmatch(ruleStr)
|
||||
if len(allMatches) != 3 {
|
||||
return fmt.Errorf("not enough members in Search&Replace, ruleStr: <%s>, matches: %v, ", ruleStr, allMatches)
|
||||
}
|
||||
var srRegexp *regexp.Regexp
|
||||
if srRegexp, err = regexp.Compile(allMatches[1]); err != nil {
|
||||
return fmt.Errorf("invalid Search&Replace subfield rule: <%s>", allMatches[1])
|
||||
}
|
||||
prsr.rsrRules = append(prsr.rsrRules, &ReSearchReplace{
|
||||
SearchRegexp: srRegexp,
|
||||
ReplaceTemplate: allMatches[2],
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseValue the field value from a string
|
||||
func (prsr *RSRParser) parseValue(value string) (out string, err error) {
|
||||
for _, rsRule := range prsr.rsrRules {
|
||||
value = rsRule.Process(value)
|
||||
}
|
||||
return prsr.converters.ConvertString(value)
|
||||
}
|
||||
|
||||
// parseValue the field value from a string
|
||||
func (prsr *RSRParser) parseValueInterface(value any) (out any, err error) {
|
||||
for _, rsRule := range prsr.rsrRules {
|
||||
value = rsRule.Process(IfaceAsString(value))
|
||||
}
|
||||
return prsr.converters.ConvertInterface(value)
|
||||
}
|
||||
|
||||
// ParseValue will parse the value out considering converters
|
||||
func (prsr *RSRParser) ParseValue(value any) (out string, err error) {
|
||||
out = prsr.path
|
||||
if out != DynamicDataPrefix &&
|
||||
strings.HasPrefix(out, DynamicDataPrefix) { // Enforce parsing of static values
|
||||
out = IfaceAsString(value)
|
||||
}
|
||||
return prsr.parseValue(out)
|
||||
}
|
||||
|
||||
// ParseDataProvider will parse the dataprovider using DPDynamicString
|
||||
func (prsr *RSRParser) ParseDataProvider(dP DataProvider) (out string, err error) {
|
||||
if prsr.dynRules != nil {
|
||||
var dynPath string
|
||||
if dynPath, err = prsr.dynRules.ParseDataProvider(dP); err != nil {
|
||||
return
|
||||
}
|
||||
var dynRSR *RSRParser
|
||||
if dynRSR, err = NewRSRParser(prsr.Rules[:prsr.dynIdxStart] + dynPath + prsr.Rules[prsr.dynIdxEnd:]); err != nil {
|
||||
return
|
||||
}
|
||||
return dynRSR.ParseDataProvider(dP)
|
||||
}
|
||||
var outStr string
|
||||
if outStr, err = DPDynamicString(prsr.path, dP); err != nil {
|
||||
return
|
||||
}
|
||||
return prsr.parseValue(outStr)
|
||||
}
|
||||
|
||||
// ParseDataProviderWithInterfaces will parse the dataprovider using DPDynamicInterface
|
||||
func (prsr *RSRParser) ParseDataProviderWithInterfaces(dP DataProvider) (out string, err error) {
|
||||
if prsr.dynRules != nil {
|
||||
var dynPath string
|
||||
if dynPath, err = prsr.dynRules.ParseDataProvider(dP); err != nil {
|
||||
return
|
||||
}
|
||||
var dynRSR *RSRParser
|
||||
if dynRSR, err = NewRSRParser(prsr.Rules[:prsr.dynIdxStart] + dynPath + prsr.Rules[prsr.dynIdxEnd:]); err != nil {
|
||||
return
|
||||
}
|
||||
return dynRSR.ParseDataProviderWithInterfaces(dP)
|
||||
}
|
||||
var outIface any
|
||||
if outIface, err = DPDynamicInterface(prsr.path, dP); err != nil {
|
||||
return
|
||||
}
|
||||
return prsr.parseValue(IfaceAsString(outIface))
|
||||
}
|
||||
|
||||
// ParseDataProviderWithInterfaces will parse the dataprovider using DPDynamicInterface
|
||||
func (prsr *RSRParser) ParseDataProviderWithInterfaces2(dP DataProvider) (out any, err error) {
|
||||
if prsr.dynRules != nil {
|
||||
var dynPath string
|
||||
if dynPath, err = prsr.dynRules.ParseDataProvider(dP); err != nil {
|
||||
return
|
||||
}
|
||||
var dynRSR *RSRParser
|
||||
if dynRSR, err = NewRSRParser(prsr.Rules[:prsr.dynIdxStart] + dynPath + prsr.Rules[prsr.dynIdxEnd:]); err != nil {
|
||||
return
|
||||
}
|
||||
return dynRSR.ParseDataProviderWithInterfaces2(dP)
|
||||
}
|
||||
var outIface any
|
||||
if outIface, err = DPDynamicInterface(prsr.path, dP); err != nil {
|
||||
return
|
||||
}
|
||||
return prsr.parseValueInterface(outIface)
|
||||
}
|
||||
|
||||
// CompileDynRule will return the compiled dynamic rule
|
||||
func (prsr *RSRParser) CompileDynRule(dP DataProvider) (p string, err error) {
|
||||
if prsr.dynRules == nil {
|
||||
return prsr.Rules, nil
|
||||
}
|
||||
var dynPath string
|
||||
if dynPath, err = prsr.dynRules.ParseDataProvider(dP); err != nil {
|
||||
return
|
||||
}
|
||||
return prsr.Rules[:prsr.dynIdxStart] + dynPath + prsr.Rules[prsr.dynIdxEnd:], nil
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of RSRParser
|
||||
func (prsr RSRParser) Clone() (cln *RSRParser) {
|
||||
cln = &RSRParser{
|
||||
Rules: prsr.Rules,
|
||||
path: prsr.path,
|
||||
dynIdxStart: prsr.dynIdxStart,
|
||||
dynIdxEnd: prsr.dynIdxEnd,
|
||||
dynRules: prsr.dynRules.Clone(),
|
||||
}
|
||||
if prsr.rsrRules != nil {
|
||||
cln.rsrRules = make([]*ReSearchReplace, len(prsr.rsrRules))
|
||||
for i, rsr := range prsr.rsrRules {
|
||||
cln.rsrRules[i] = rsr.Clone()
|
||||
}
|
||||
}
|
||||
if prsr.converters != nil {
|
||||
cln.converters = make(DataConverters, len(prsr.converters))
|
||||
// we can't modify the convertor only overwrite it
|
||||
// safe to copy it's value
|
||||
copy(cln.converters, prsr.converters)
|
||||
}
|
||||
return
|
||||
}
|
||||
980
utils/rsrparser_test.go
Normal file
980
utils/rsrparser_test.go
Normal file
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRSRParsers(t *testing.T) {
|
||||
ruleStr := `Value1;Value2;~Header3;~Header4:s/a/${1}b/{*duration_seconds&*round:2};Value5{*duration_seconds&*round:2}`
|
||||
eRSRParsers := RSRParsers{
|
||||
&RSRParser{Rules: "Value1", path: "Value1"},
|
||||
&RSRParser{Rules: "Value2", path: "Value2"},
|
||||
&RSRParser{Rules: "~Header3", path: "~Header3", rsrRules: make([]*ReSearchReplace, 0)},
|
||||
&RSRParser{Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}",
|
||||
path: "~Header4",
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`a`),
|
||||
ReplaceTemplate: "${1}b"}},
|
||||
converters: DataConverters{NewDataConverterMustCompile("*duration_seconds"),
|
||||
NewDataConverterMustCompile("*round:2")},
|
||||
},
|
||||
|
||||
&RSRParser{Rules: "Value5{*duration_seconds&*round:2}",
|
||||
path: "Value5",
|
||||
converters: DataConverters{NewDataConverterMustCompile("*duration_seconds"),
|
||||
NewDataConverterMustCompile("*round:2")},
|
||||
},
|
||||
}
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eRSRParsers, rsrParsers) {
|
||||
t.Errorf("expecting: %+v, received: %+v", eRSRParsers, rsrParsers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserCompile(t *testing.T) {
|
||||
ePrsr := &RSRParser{
|
||||
Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}",
|
||||
path: "~Header4",
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`a`),
|
||||
ReplaceTemplate: "${1}b"}},
|
||||
converters: DataConverters{NewDataConverterMustCompile("*duration_seconds"),
|
||||
NewDataConverterMustCompile("*round:2")},
|
||||
}
|
||||
prsr := &RSRParser{
|
||||
Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}",
|
||||
}
|
||||
if err := prsr.Compile(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(ePrsr, prsr) {
|
||||
t.Errorf("expecting: %+v, received: %+v", ePrsr, prsr)
|
||||
}
|
||||
|
||||
prsr = &RSRParser{
|
||||
Rules: "~*req.Field{*}",
|
||||
}
|
||||
expErr := "invalid converter value in string: <*>, err: unsupported converter definition: <*>"
|
||||
if err := prsr.Compile(); err == nil || err.Error() != expErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserConstant(t *testing.T) {
|
||||
rule := "cgrates.org"
|
||||
rsrParsers, err := NewRSRParsers(rule, InfieldSep)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
}
|
||||
if out, err := rsrParsers.ParseValue(""); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out != "cgrates.org" {
|
||||
t.Errorf("expecting: cgrates.org , received: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserNotConstant(t *testing.T) {
|
||||
rule := "~Header1;~Header2"
|
||||
rsrParsers, err := NewRSRParsers(rule, InfieldSep)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
}
|
||||
if out, err := rsrParsers.ParseValue(""); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out != "" {
|
||||
t.Errorf("expecting: EmptyString , received: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsersConstant(t *testing.T) {
|
||||
ruleStr := "`>;q=0.7;expires=3600`"
|
||||
eRSRParsers := RSRParsers{
|
||||
&RSRParser{Rules: ">;q=0.7;expires=3600", path: ">;q=0.7;expires=3600"},
|
||||
}
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eRSRParsers, rsrParsers) {
|
||||
t.Errorf("expecting: %+v, received: %+v", eRSRParsers, rsrParsers)
|
||||
} else if out, err := rsrParsers.ParseDataProvider(MapStorage{}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := ">;q=0.7;expires=3600"; out != expected {
|
||||
t.Errorf("Expected %+v ,received %+v", expected, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsersConstant2(t *testing.T) {
|
||||
ruleStr := "constant;something`>;q=0.7;expires=3600`new;constant"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out, err := rsrParsers.ParseDataProvider(MapStorage{}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := "constantsomething>;q=0.7;expires=3600newconstant"; out != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, out)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`;constant"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out, err := rsrParsers.ParseDataProvider(MapStorage{}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := "constant>;q=0.7;expires=3600constant"; out != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, out)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`constant"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out, err := rsrParsers.ParseDataProvider(MapStorage{}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := "constant>;q=0.7;expires=3600constant"; out != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, out)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`;"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out, err := rsrParsers.ParseDataProvider(MapStorage{}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := "constant>;q=0.7;expires=3600"; out != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, out)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600constant"
|
||||
if _, err := NewRSRParsers(ruleStr, InfieldSep); err == nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`;~*req.Account"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
} else if _, err := rsrParsers.ParseDataProvider(MapStorage{}); err != ErrNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRSRParserCompileConstant(t *testing.T) {
|
||||
ePrsr := &RSRParser{
|
||||
Rules: ":>;q=0.7;expires=3600",
|
||||
|
||||
path: ":>;q=0.7;expires=3600",
|
||||
}
|
||||
prsr := &RSRParser{
|
||||
Rules: ":>;q=0.7;expires=3600",
|
||||
}
|
||||
if err := prsr.Compile(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(ePrsr, prsr) {
|
||||
t.Errorf("expecting: %+v, received: %+v", ePrsr, prsr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsersParseDataProviderWithInterfaces(t *testing.T) {
|
||||
ruleStr := "~;*accounts.;~*req.Account"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if out, err := rsrParsers.ParseDataProviderWithInterfaces(
|
||||
MapStorage{
|
||||
MetaReq: MapStorage{AccountField: "1001"},
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
} else if expected := "~*accounts.1001"; out != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, out)
|
||||
}
|
||||
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`;~*req.Account"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if _, err := rsrParsers.ParseDataProviderWithInterfaces(MapStorage{}); err != ErrNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsersFromSlice(t *testing.T) {
|
||||
if _, err := NewRSRParsersFromSlice([]string{""}); err == nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if _, err := NewRSRParsersFromSlice([]string{"~*req.Account{*"}); err == nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsersMustCompile(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic on wrong rule")
|
||||
}
|
||||
}()
|
||||
NewRSRParsersMustCompile("~*req.Account{*", ";")
|
||||
}
|
||||
|
||||
func TestRSRParserGetRule(t *testing.T) {
|
||||
ruleStr := "constant;~*req.Account"
|
||||
if rsrParsers, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if rule := rsrParsers.GetRule(); rule != ruleStr {
|
||||
t.Errorf("Expected: %q received: %q", ruleStr, rule)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParsersCompile(t *testing.T) {
|
||||
prsrs := RSRParsers{&RSRParser{
|
||||
Rules: ":>;q=0.7;expires=3600",
|
||||
}}
|
||||
ePrsr := RSRParsers{&RSRParser{
|
||||
Rules: ":>;q=0.7;expires=3600",
|
||||
|
||||
path: ":>;q=0.7;expires=3600",
|
||||
}}
|
||||
if err := prsrs.Compile(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(prsrs, ePrsr) {
|
||||
t.Errorf("Expected %+v received %+v", ePrsr, prsrs)
|
||||
}
|
||||
prsrs = RSRParsers{&RSRParser{
|
||||
Rules: "~*req.Account{*unuportedConverter}",
|
||||
}}
|
||||
if err := prsrs.Compile(); err == nil {
|
||||
t.Error("Expected error received:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParsersParseValue(t *testing.T) {
|
||||
rsrParsers, err := NewRSRParsers("~*req.Account{*round}", InfieldSep)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
}
|
||||
if _, err = rsrParsers.ParseValue("A"); err == nil {
|
||||
t.Error("Expected error received:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParserMustCompile(t *testing.T) {
|
||||
rsr := NewRSRParserMustCompile("~*req.Account")
|
||||
ePrsr := &RSRParser{
|
||||
Rules: "~*req.Account",
|
||||
rsrRules: make([]*ReSearchReplace, 0),
|
||||
path: "~*req.Account",
|
||||
}
|
||||
if !reflect.DeepEqual(rsr, ePrsr) {
|
||||
t.Errorf("Expected %+v received %+v", ePrsr, rsr)
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic on wrong rule")
|
||||
}
|
||||
}()
|
||||
NewRSRParserMustCompile("~*req.Account{*")
|
||||
}
|
||||
|
||||
func TestRSRParserAttrName(t *testing.T) {
|
||||
rsr := NewRSRParserMustCompile("~*req.Account")
|
||||
expected := "*req.Account"
|
||||
if attr := rsr.AttrName(); attr != expected {
|
||||
t.Errorf("Expected: %q received: %q", expected, attr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserCompile3(t *testing.T) {
|
||||
rsr := &RSRParser{Rules: "~*req.Account:s/(a+)/${1}s"}
|
||||
if err := rsr.Compile(); err == nil {
|
||||
t.Error("Expected error received:", err)
|
||||
}
|
||||
|
||||
rsr = &RSRParser{Rules: "~*req.Account:s/*/${1}s/"}
|
||||
if err := rsr.Compile(); err == nil {
|
||||
t.Error("Expected error received:", err)
|
||||
}
|
||||
}
|
||||
func TestRSRParserDynamic(t *testing.T) {
|
||||
ePrsr := &RSRParser{
|
||||
Rules: "~*opts.<~*opts.*originID;~*req.RunID;-Cost>",
|
||||
dynRules: NewRSRParsersMustCompile("~*opts.*originID;~*req.RunID;-Cost", ";"),
|
||||
dynIdxStart: 7,
|
||||
dynIdxEnd: 43,
|
||||
}
|
||||
prsr := &RSRParser{
|
||||
Rules: "~*opts.<~*opts.*originID;~*req.RunID;-Cost>",
|
||||
}
|
||||
if err := prsr.Compile(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(ePrsr, prsr) {
|
||||
t.Errorf("expecting: %+v, received: %+v", ePrsr, prsr)
|
||||
}
|
||||
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "Uniq",
|
||||
"Uniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
if out, err := prsr.ParseDataProvider(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "10" {
|
||||
t.Errorf("Expected 10 received: %q", out)
|
||||
}
|
||||
|
||||
prsr = &RSRParser{
|
||||
Rules: "~*opts.<~*opts.*originID;~*req.RunID;-Cost{*}>",
|
||||
}
|
||||
expErr := "invalid converter value in string: <*>, err: unsupported converter definition: <*>"
|
||||
if err := prsr.Compile(); err == nil || err.Error() != expErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRSRParserDynamic2(t *testing.T) {
|
||||
prsr, err := NewRSRParsersFromSlice([]string{"~*opts.<~*opts.*originID;~*req.RunID;-Cos>t", "s"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
"originIDUniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
if out, err := prsr.ParseDataProvider(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "10s" {
|
||||
t.Errorf("Expected 10s received: %q", out)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.<~*opts.*originID;~*req.RunID;-Cos>t", "s"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out, err := prsr.ParseDataProvider(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "2.10s" {
|
||||
t.Errorf("Expected 2.10s received: %q", out)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.<~*opts.*originID;~*req.RunID;-Cost>"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out, err := prsr.ParseDataProvider(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "2.10" {
|
||||
t.Errorf("Expected 2.10 received: %q", out)
|
||||
}
|
||||
|
||||
dP = MapStorage{
|
||||
MetaReq: MapStorage{},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
},
|
||||
}
|
||||
if _, err := prsr.ParseDataProvider(dP); err != ErrNotFound {
|
||||
t.Errorf("Expected error %s, received: %v", ErrNotFound, err)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.*originID<~*opts.Converter>"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dP = MapStorage{
|
||||
MetaReq: MapStorage{},
|
||||
MetaOpts: MapStorage{
|
||||
"Converter": "{*",
|
||||
MetaOriginID: "originIDUniq",
|
||||
},
|
||||
}
|
||||
if _, err := prsr.ParseDataProvider(dP); err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserDynamic3(t *testing.T) {
|
||||
prsr, err := NewRSRParsersFromSlice([]string{"2.", "~*opts.<~*opts.*originID;~*req.RunID>-Cost", "-", "~*req.<~*req.UnitField>"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
|
||||
"UnitField": "Unit",
|
||||
"Unit": "MB",
|
||||
"IP": "127.0.0.1",
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
|
||||
MetaOriginID: "originIDUniq",
|
||||
"originIDUniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
|
||||
if out, err := prsr.ParseDataProvider(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "2.10-MB" {
|
||||
t.Errorf("Expected 2.10-MB received: %q", out)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRSRParserParseDataProviderWithInterfaces(t *testing.T) {
|
||||
prsr, err := NewRSRParsersFromSlice([]string{"~*opts.<~*opts.*originID;~*req.RunID;-Cos>t", "s"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
"originIDUniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
if out, err := prsr.ParseDataProviderWithInterfaces(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "10s" {
|
||||
t.Errorf("Expected 10s received: %q", out)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.<~*opts.*originID;~*req.RunID;-Cos>t", "s"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out, err := prsr.ParseDataProviderWithInterfaces(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "210s" {
|
||||
t.Errorf("Expected 210s received: %q", out)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.<~*opts.*originID;~*req.RunID;-Cost>"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out, err := prsr.ParseDataProviderWithInterfaces(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "210" {
|
||||
t.Errorf("Expected 210 received: %q", out)
|
||||
}
|
||||
|
||||
dP = MapStorage{
|
||||
MetaReq: MapStorage{},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
},
|
||||
}
|
||||
if _, err := prsr.ParseDataProviderWithInterfaces(dP); err != ErrNotFound {
|
||||
t.Errorf("Expected error %s, received: %v", ErrNotFound, err)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParsersFromSlice([]string{"2.", "~*opts.*originID<~*opts.Converter>"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dP = MapStorage{
|
||||
MetaReq: MapStorage{},
|
||||
MetaOpts: MapStorage{
|
||||
"Converter": "{*",
|
||||
MetaOriginID: "originIDUniq",
|
||||
},
|
||||
}
|
||||
if _, err := prsr.ParseDataProviderWithInterfaces(dP); err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserCompileDynRule(t *testing.T) {
|
||||
prsr, err := NewRSRParser("~*opts.<~*opts.*originID;~*req.RunID;-Cos>t")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
"originIDUniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
if out, err := prsr.CompileDynRule(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "~*opts.originIDUniq*default-Cost" {
|
||||
t.Errorf("Expected ~*opts.originIDUniq*default-Cost received: %q", out)
|
||||
}
|
||||
|
||||
dP = MapStorage{
|
||||
MetaReq: MapStorage{},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "originIDUniq",
|
||||
},
|
||||
}
|
||||
if _, err := prsr.CompileDynRule(dP); err != ErrNotFound {
|
||||
t.Errorf("Expected error %s, received: %v", ErrNotFound, err)
|
||||
}
|
||||
|
||||
prsr, err = NewRSRParser("~*opts.*originID")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out, err := prsr.CompileDynRule(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != "~*opts.*originID" {
|
||||
t.Errorf("Expected ~*opts.*originID received: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParsersGetIfaceFromValues(t *testing.T) {
|
||||
dp := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
Category: "call",
|
||||
},
|
||||
}
|
||||
exp := []any{"*rated", "call"}
|
||||
if rply, err := NewRSRParsersMustCompile("*rated;~*req.Category", InfieldSep).GetIfaceFromValues(dp); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(exp, rply) {
|
||||
t.Errorf("Expecting %q, received: %q", exp, rply)
|
||||
}
|
||||
if _, err := NewRSRParsersMustCompile("*rated;~req.Category", InfieldSep).GetIfaceFromValues(MapStorage{}); err != ErrNotFound {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParser(t *testing.T) {
|
||||
// Normal case
|
||||
rulesStr := `~sip_redirected_to:s/sip:\+49(\d+)@/0$1/`
|
||||
expRSRField1 := &RSRParser{
|
||||
path: "~sip_redirected_to",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{
|
||||
{
|
||||
SearchRegexp: regexp.MustCompile(`sip:\+49(\d+)@`),
|
||||
ReplaceTemplate: "0$1",
|
||||
},
|
||||
},
|
||||
converters: nil,
|
||||
}
|
||||
if rsrField, err := NewRSRParser(rulesStr); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(expRSRField1, rsrField) {
|
||||
t.Errorf("Expecting: %+v, received: %+v",
|
||||
expRSRField1, rsrField)
|
||||
}
|
||||
|
||||
// with dataConverters
|
||||
rulesStr = `~sip_redirected_to:s/sip:\+49(\d+)@/0$1/{*duration_seconds&*round:5:*middle}`
|
||||
expRSRField := &RSRParser{
|
||||
path: "~sip_redirected_to",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`sip:\+49(\d+)@`),
|
||||
ReplaceTemplate: "0$1",
|
||||
}},
|
||||
converters: []DataConverter{
|
||||
new(DurationSecondsConverter),
|
||||
&RoundConverter{Decimals: 5, Method: "*middle"},
|
||||
},
|
||||
}
|
||||
if rsrField, err := NewRSRParser(rulesStr); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(expRSRField, rsrField) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", expRSRField, rsrField)
|
||||
}
|
||||
// One extra separator but escaped
|
||||
rulesStr = `~sip_redirected_to:s/sip:\+49(\d+)\/@/0$1/`
|
||||
expRSRField3 := &RSRParser{
|
||||
path: "~sip_redirected_to",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`sip:\+49(\d+)\/@`),
|
||||
ReplaceTemplate: "0$1",
|
||||
}},
|
||||
}
|
||||
if rsrField, err := NewRSRParser(rulesStr); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(expRSRField3, rsrField) {
|
||||
t.Errorf("Expecting: %v, received: %v", expRSRField3, rsrField)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewRSRParserDDz(t *testing.T) {
|
||||
rulesStr := `~effective_caller_id_number:s/(\d+)/+$1/`
|
||||
expectRSRField := &RSRParser{
|
||||
path: "~effective_caller_id_number",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`(\d+)`),
|
||||
ReplaceTemplate: "+$1",
|
||||
}},
|
||||
}
|
||||
if rsrField, err := NewRSRParser(rulesStr); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rsrField, expectRSRField) {
|
||||
t.Errorf("Unexpected RSRField received: %v", rsrField)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParserIvo(t *testing.T) {
|
||||
rulesStr := `~cost_details:s/MatchedDestId":".+_(\s\s\s\s\s)"/$1/`
|
||||
expectRSRField := &RSRParser{
|
||||
path: "~cost_details",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`MatchedDestId":".+_(\s\s\s\s\s)"`),
|
||||
ReplaceTemplate: "$1",
|
||||
}},
|
||||
}
|
||||
if rsrField, err := NewRSRParser(rulesStr); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rsrField, expectRSRField) {
|
||||
t.Errorf("Unexpected RSRField received: %v", rsrField)
|
||||
}
|
||||
if _, err := NewRSRParser(`~account:s/^[A-Za-z0-9]*[c|a]\d{4}$/S/:s/^[A-Za-z0-9]*n\d{4}$/C/:s/^\d{10}$//`); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertPlusNationalAnd00(t *testing.T) {
|
||||
rulesStr := `~effective_caller_id_number:s/\+49(\d+)/0$1/:s/\+(\d+)/00$1/`
|
||||
expectRSRField := &RSRParser{
|
||||
path: "~effective_caller_id_number",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{
|
||||
{
|
||||
SearchRegexp: regexp.MustCompile(`\+49(\d+)`),
|
||||
ReplaceTemplate: "0$1",
|
||||
},
|
||||
{
|
||||
SearchRegexp: regexp.MustCompile(`\+(\d+)`),
|
||||
ReplaceTemplate: "00$1",
|
||||
},
|
||||
},
|
||||
}
|
||||
rsrField, err := NewRSRParser(rulesStr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rsrField, expectRSRField) {
|
||||
t.Errorf("Expecting: %v, received: %v", expectRSRField, rsrField)
|
||||
}
|
||||
if parsedVal, err := rsrField.ParseValue("+4986517174963"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != "086517174963" {
|
||||
t.Errorf("Expecting: 086517174963, received: %s", parsedVal)
|
||||
}
|
||||
if parsedVal, err := rsrField.ParseValue("+3186517174963"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != "003186517174963" {
|
||||
t.Errorf("Expecting: 003186517174963, received: %s", parsedVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertDurToSecs(t *testing.T) {
|
||||
rulesStr := `~9:s/^(\d+)$/${1}s/`
|
||||
expectRSRField := &RSRParser{
|
||||
path: "~9",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`^(\d+)$`),
|
||||
ReplaceTemplate: "${1}s",
|
||||
}},
|
||||
}
|
||||
rsrField, err := NewRSRParser(rulesStr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rsrField, expectRSRField) {
|
||||
t.Errorf("Expecting: %v, received: %v", expectRSRField, rsrField)
|
||||
}
|
||||
if parsedVal, err := rsrField.ParseValue("640113"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != "640113s" {
|
||||
t.Errorf("Expecting: 640113s, received: %s", parsedVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefix164(t *testing.T) {
|
||||
rulesStr := `~0:s/^([1-9]\d+)$/+$1/`
|
||||
expectRSRField := &RSRParser{
|
||||
path: "~0",
|
||||
Rules: rulesStr,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`^([1-9]\d+)$`),
|
||||
ReplaceTemplate: "+$1",
|
||||
}},
|
||||
}
|
||||
rsrField, err := NewRSRParser(rulesStr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rsrField, expectRSRField) {
|
||||
t.Errorf("Expecting: %v, received: %v", expectRSRField, rsrField)
|
||||
}
|
||||
if parsedVal, err := rsrField.ParseValue("4986517174960"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != "+4986517174960" {
|
||||
t.Errorf("Expecting: +4986517174960, received: %s", parsedVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRSRParsers2(t *testing.T) {
|
||||
fieldsStr1 := `~account:s/^\w+[mpls]\d{6}$//;~subject:s/^0\d{9}$//;~mediation_runid:s/^default$/default/`
|
||||
rsrFld1, err := NewRSRParser(`~account:s/^\w+[mpls]\d{6}$//`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rsrFld2, err := NewRSRParser(`~subject:s/^0\d{9}$//`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rsrFld4, err := NewRSRParser(`~mediation_runid:s/^default$/default/`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eRSRFields := RSRParsers{rsrFld1, rsrFld2, rsrFld4}
|
||||
if rsrFlds, err := NewRSRParsers(fieldsStr1, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
} else if !reflect.DeepEqual(eRSRFields, rsrFlds) {
|
||||
t.Errorf("Expecting: %v, received: %v", eRSRFields, rsrFlds)
|
||||
}
|
||||
fields := `host,~sip_redirected_to:s/sip:\+49(\d+)@/0$1/,destination`
|
||||
expectParsedFields := RSRParsers{
|
||||
{
|
||||
path: "host",
|
||||
Rules: "host",
|
||||
},
|
||||
{
|
||||
path: "~sip_redirected_to",
|
||||
Rules: `~sip_redirected_to:s/sip:\+49(\d+)@/0$1/`,
|
||||
rsrRules: []*ReSearchReplace{{
|
||||
SearchRegexp: regexp.MustCompile(`sip:\+49(\d+)@`),
|
||||
ReplaceTemplate: "0$1",
|
||||
}},
|
||||
},
|
||||
{
|
||||
path: "destination",
|
||||
Rules: "destination",
|
||||
},
|
||||
}
|
||||
if parsedFields, err := NewRSRParsers(fields, FieldsSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(parsedFields, expectParsedFields) {
|
||||
t.Errorf("Expected: %s ,received: %s ", ToJSON(expectParsedFields), ToJSON(parsedFields))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCdrcDn1(t *testing.T) {
|
||||
rl, err := NewRSRParser(`~1:s/^00(\d+)(?:[a-zA-Z].{3})*0*([1-9]\d+)$/+$1$2/:s/^\+49(18\d{2})$/+491400$1/`)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
}
|
||||
if parsed, err := rl.ParseValue("0049ABOC0630415354"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsed != "+49630415354" {
|
||||
t.Errorf("Expecting: +49630415354, received: %s", parsed)
|
||||
}
|
||||
if parsed2, err := rl.ParseValue("00491888"); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsed2 != "+4914001888" {
|
||||
t.Errorf("Expecting: +4914001888, received: %s", parsed2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRCostDetails(t *testing.T) {
|
||||
fieldsStr1 := `{"Category":"default_route","Tenant":"demo.cgrates.org","Subject":"voxbeam_premium","Account":"6335820713","Destination":"15143606781","ToR":"*voice","Cost":0.0007,"Timespans":[{"TimeStart":"2015-08-30T21:46:54Z","TimeEnd":"2015-08-30T21:47:06Z","Cost":0.00072,"RateInterval":{"Rating":{"ConnectFee":0,"RoundingMethod":"*middle","RoundingDecimals":5,"MaxCost":0,"MaxCostStrategy":"0","Rates":[{"GroupIntervalStart":0,"Value":0.0036,"RateIncrement":6000000000,"RateUnit":60000000000}]},"Weight":10},"DurationIndex":12000000000,"Increments":[{"Duration":6000000000,"Cost":0.00036,"BalanceInfo":{"UnitBalanceUuid":"","MoneyBalanceUuid":"40adda88-25d3-4009-b928-f39d61590439","AccountId":"*out:demo.cgrates.org:6335820713"},"BalanceRateInterval":null,"UnitInfo":null,"CompressFactor":2}],"MatchedSubject":"*out:demo.cgrates.org:default_route:voxbeam_premium","MatchedPrefix":"1514","MatchedDestId":"Canada","RatingPlanId":"RP_VOXBEAM_PREMIUM"}]}`
|
||||
rsrField, err := NewRSRParser(`~cost_details:s/"MatchedDestId":"(\w+)"/${1}/`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if parsedVal, err := rsrField.ParseValue(fieldsStr1); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != "Canada" {
|
||||
t.Errorf("Expecting: Canada, received: %s", parsedVal)
|
||||
}
|
||||
fieldsStr2 := `{"Category":"call","Tenant":"sip.test.cgrates.org","Subject":"dan","Account":"dan","Destination":"+4986517174963","ToR":"*voice","Cost":0,"Timespans":[{"TimeStart":"2015-05-13T15:03:34+02:00","TimeEnd":"2015-05-13T15:03:38+02:00","Cost":0,"RateInterval":{"Rating":{"ConnectFee":0,"RoundingMethod":"*middle","RoundingDecimals":4,"MaxCost":0,"MaxCostStrategy":"","Rates":[{"GroupIntervalStart":0,"Value":0,"RateIncrement":1000000000,"RateUnit":60000000000}]},"Weight":10},"DurationIndex":4000000000,"Increments":[{"Duration":1000000000,"Cost":0,"BalanceInfo":{"Unit":null,"Monetary":null,"AccountID":""},"CompressFactor":4}],"RoundIncrement":null,"MatchedSubject":"*out:sip.test.cgrates.org:call:*any","MatchedPrefix":"+31800","MatchedDestId":"CST_49800_DE080","RatingPlanId":"ISC_V","CompressFactor":1}],"RatedUsage":4}`
|
||||
rsrField, err = NewRSRParser(`~CostDetails:s/"MatchedDestId":.*_(\w{5})/${1}/:s/"MatchedDestId":"INTERNAL"/ON010/`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
eMatch := "DE080"
|
||||
if parsedVal, err := rsrField.ParseValue(fieldsStr2); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsedVal != eMatch {
|
||||
t.Errorf("Expecting: <%s>, received: <%s>", eMatch, parsedVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRFldParse(t *testing.T) {
|
||||
// with dataConverters
|
||||
rulesStr := `~Usage:s/(\d+)/${1}ms/{*duration_seconds&*round:1:*middle}`
|
||||
rsrField, err := NewRSRParser(rulesStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eOut := "2.2"
|
||||
if out, err := rsrField.ParseValue("2210"); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != eOut {
|
||||
t.Errorf("expecting: %s, received: %s", eOut, out)
|
||||
}
|
||||
rulesStr = `~Usage:s/(\d+)/${1}ms/{*duration_seconds&*round}`
|
||||
if rsrField, err = NewRSRParser(rulesStr); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
eOut = "2"
|
||||
if out, err := rsrField.ParseValue("2210"); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != eOut {
|
||||
t.Errorf("expecting: %s, received: %s", eOut, out)
|
||||
}
|
||||
rulesStr = `~Usage{*duration_seconds}`
|
||||
rsrField, err = NewRSRParser(rulesStr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
eOut = "10"
|
||||
if out, err := rsrField.ParseValue("10000000000"); err != nil {
|
||||
t.Error(err)
|
||||
} else if out != eOut {
|
||||
t.Errorf("expecting: %s, received: %s", eOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParsersClone(t *testing.T) {
|
||||
rsrs, err := NewRSRParsers(`~subject:s/^0\d{9}$//{*duration}`, InfieldSep)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cln := rsrs.Clone()
|
||||
if !reflect.DeepEqual(rsrs, cln) {
|
||||
t.Errorf("Expected: %+v\nReceived: %+v", ToJSON(rsrs), ToJSON(cln))
|
||||
}
|
||||
cln[0].converters[0] = NewDataConverterMustCompile(MetaIP2Hex)
|
||||
if reflect.DeepEqual(cln[0].converters[0], rsrs[0].converters[0]) {
|
||||
t.Errorf("Expected clone to not modify the cloned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParsersParseDataProviderWithInterfaces2(t *testing.T) {
|
||||
ruleStr := "~;*accounts.;~*req.Account"
|
||||
if prsrs, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if rcv, err := prsrs.ParseDataProviderWithInterfaces2(MapStorage{
|
||||
MetaReq: MapStorage{AccountField: "1001"},
|
||||
}); err != nil {
|
||||
t.Errorf("Expected error <nil>, Received error <%v>", err)
|
||||
|
||||
} else if expected := "~*accounts.1001"; rcv != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, rcv)
|
||||
}
|
||||
ruleStr = "constant;`>;q=0.7;expires=3600`;~*req.Account"
|
||||
if prsrs, err := NewRSRParsers(ruleStr, InfieldSep); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if _, err := prsrs.ParseDataProviderWithInterfaces2(MapStorage{}); err != ErrNotFound {
|
||||
t.Errorf("Expected error <%v>, Received error <%v>", ErrNotFound, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRSRParserParseValueInterface(t *testing.T) {
|
||||
|
||||
prsr := &RSRParser{
|
||||
rsrRules: []*ReSearchReplace{
|
||||
{
|
||||
SearchRegexp: regexp.MustCompile(`a`),
|
||||
ReplaceTemplate: "${1}b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if rcv, err := prsr.parseValueInterface(""); err != nil {
|
||||
t.Errorf("Expected error <nil>, Received error <%v>", err)
|
||||
|
||||
} else if expected := ""; rcv != expected {
|
||||
t.Errorf("Expected %q ,received %q", expected, rcv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSRParserParseDataProviderWithInterfaces2(t *testing.T) {
|
||||
prsr := &RSRParser{
|
||||
dynRules: NewRSRParsersMustCompile("~*opts.*originID;~*req.RunID;-Cost", ";"),
|
||||
}
|
||||
|
||||
if _, err := prsr.ParseDataProviderWithInterfaces2(MapStorage{
|
||||
MetaReq: MapStorage{AccountField: "1001"},
|
||||
}); err != ErrNotFound {
|
||||
t.Errorf("Expected error <%v>, Received error <%v>", ErrNotFound, err)
|
||||
|
||||
}
|
||||
|
||||
rsrParser, _ := NewRSRParsers("~*opts.*originI;;;D;~*req.RunID;-Cost", "")
|
||||
prsr = &RSRParser{
|
||||
Rules: "~*opts.<~*opts.*originID;~*req.RunID;-Cost{*}>",
|
||||
dynRules: rsrParser,
|
||||
}
|
||||
expErr := "invalid converter value in string: <*>, err: unsupported converter definition: <*>"
|
||||
if _, err := prsr.ParseDataProviderWithInterfaces2(MapStorage{
|
||||
MetaReq: MapStorage{AccountField: "1001"},
|
||||
}); err.Error() != expErr {
|
||||
t.Errorf("Expected error <%v>, Received error <%v>", expErr, err.Error())
|
||||
|
||||
}
|
||||
|
||||
prsr = &RSRParser{
|
||||
dynRules: NewRSRParsersMustCompile("~*opts.*originID;~*req.RunID;-Cost", ";"),
|
||||
}
|
||||
dP := MapStorage{
|
||||
MetaReq: MapStorage{
|
||||
|
||||
RunID: MetaDefault,
|
||||
},
|
||||
MetaOpts: MapStorage{
|
||||
MetaOriginID: "Uniq",
|
||||
"Uniq*default-Cost": 10,
|
||||
},
|
||||
}
|
||||
exp := "Uniq*default-Cost"
|
||||
if rcv, err := prsr.ParseDataProviderWithInterfaces2(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if rcv != exp {
|
||||
t.Errorf("Expected <%v>, \nReceived <%v>", exp, rcv)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user