Added DynRSRParser functionality

This commit is contained in:
Trial97
2020-06-30 17:29:55 +03:00
committed by Dan Christian Bogos
parent b79524e9ab
commit f9454bfb4c
4 changed files with 134 additions and 26 deletions

View File

@@ -81,6 +81,11 @@ func NewRSRParsers(parsersRules string, rsrSeparator string) (prsrs RSRParsers,
}
return NewRSRParsersFromSlice(splitedRule)
}
if dynIdxStart := strings.IndexByte(parsersRules, '<'); dynIdxStart != -1 {
if dynIdxEnd := strings.IndexByte(parsersRules[dynIdxStart:], '>'); dynIdxEnd != -1 {
return NewDynRSRParser(parsersRules, rsrSeparator, dynIdxStart, dynIdxStart+dynIdxEnd)
}
}
return NewRSRParsersFromSlice(strings.Split(parsersRules, rsrSeparator))
}
@@ -186,6 +191,10 @@ type RSRParser struct {
path string // instruct extracting info out of header in event
rsrRules []*utils.ReSearchReplace // rules to use when parsing value
converters utils.DataConverters // set of converters to apply on output
dynIdxStart int
dynIdxEnd int
dynRules RSRParsers
}
// AttrName exports the attribute name of the RSRParser
@@ -196,6 +205,20 @@ func (prsr *RSRParser) AttrName() string {
// Compile parses Rules string and repopulates other fields
func (prsr *RSRParser) Compile() (err error) {
parserRules := prsr.Rules
if dynIdxStart := strings.IndexByte(parserRules, '<'); dynIdxStart != -1 {
if dynIdxEnd := strings.IndexByte(parserRules[dynIdxStart:], '>'); dynIdxEnd != -1 {
var dynrules RSRParsers
if dynrules, err = NewRSRParsers(parserRules[dynIdxStart+1:dynIdxStart+dynIdxEnd],
CgrConfig().GeneralCfg().RSRSep); err != nil {
return
}
prsr.dynRules = dynrules
prsr.dynIdxStart = dynIdxStart
prsr.dynIdxEnd = dynIdxStart + dynIdxEnd
return
}
}
if idxConverters := strings.Index(parserRules, "{*"); idxConverters != -1 { // converters in the string
if !strings.HasSuffix(parserRules, "}") {
return fmt.Errorf("invalid converter terminator in rule: <%s>",
@@ -271,6 +294,17 @@ func (prsr *RSRParser) ParseValue(value interface{}) (out string, err error) {
}
func (prsr *RSRParser) ParseDataProvider(dP utils.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+1:]); err != nil {
return
}
return dynRSR.ParseDataProvider(dP)
}
var outStr string
if outStr, err = utils.DPDynamicString(prsr.path, dP); err != nil {
return
@@ -279,9 +313,79 @@ func (prsr *RSRParser) ParseDataProvider(dP utils.DataProvider) (out string, err
}
func (prsr *RSRParser) ParseDataProviderWithInterfaces(dP utils.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+1:]); err != nil {
return
}
return dynRSR.ParseDataProviderWithInterfaces(dP)
}
var outIface interface{}
if outIface, err = utils.DPDynamicInterface(prsr.path, dP); err != nil {
return
}
return prsr.parseValue(utils.IfaceAsString(outIface))
}
func NewDynRSRParser(parsersRules string, sep string, idxStart, idxEnd int) (prsrs RSRParsers, err error) {
lastSepIdxBDyn := strings.LastIndex(parsersRules[:idxStart], sep)
if lastSepIdxBDyn != -1 {
if prsrs, err = NewRSRParsersFromSlice(strings.Split(parsersRules[:lastSepIdxBDyn], sep)); err != nil {
return // an error ocured before the dynamic path
}
}
sepIdx := strings.Index(parsersRules[idxEnd:], sep)
if sepIdx == -1 { // not found any other separtor so is the last rule
sepIdx = len(parsersRules)
}
dynRuleStr := parsersRules[lastSepIdxBDyn+1 : sepIdx] // this should contain the rule with the dynamic information
var dynrules RSRParsers
if dynrules, err = NewRSRParsers(parsersRules[idxStart+1:idxEnd], sep); err != nil {
return
}
// add
prsrs = append(prsrs, &RSRParser{
Rules: dynRuleStr,
dynRules: dynrules,
dynIdxStart: idxStart,
dynIdxEnd: idxEnd,
})
if sepIdx == len(parsersRules) { // there are no more rules so return
return
}
lastRules := parsersRules[sepIdx+1:]
if dynIdxStart := strings.IndexByte(lastRules, '<'); dynIdxStart != -1 {
if dynIdxEnd := strings.IndexByte(lastRules[dynIdxStart:], '>'); dynIdxEnd != -1 {
var lastrsr RSRParsers
if lastrsr, err = NewDynRSRParser(lastRules, sep,
dynIdxStart, dynIdxStart+dynIdxEnd); err != nil {
return
}
prsrs = append(prsrs, lastrsr...)
// alternative for append in order to not create a slice with unused capacity:
// cp := make(RSRParsers, len(prsrs)+len(lastrsr))
// copy(cp, prsrs)
// copy(cp[len(prsrs):], lastrsr)
// prsrs = cp
return
}
}
// no dynamic path anymore
var lastrsr RSRParsers
if lastrsr, err = NewRSRParsersFromSlice(strings.Split(lastRules, sep)); err != nil {
return
}
prsrs = append(prsrs, lastrsr...)
// alternative for append in order to not create a slice with unused capacity:
// cp := make(RSRParsers, len(prsrs)+len(lastrsr))
// copy(cp, prsrs)
// copy(cp[len(prsrs):], lastrsr)
// prsrs = cp
return
}

View File

@@ -324,3 +324,33 @@ func TestRSRParserCompile3(t *testing.T) {
t.Error("Expected error received:", err)
}
}
func TestRSRParserDynamic(t *testing.T) {
ePrsr := &RSRParser{
Rules: "~*req.<~*req.CGRID;~*req.RunID;-Cost>",
dynRules: NewRSRParsersMustCompile("~*req.CGRID;~*req.RunID;-Cost", ";"),
dynIdxStart: 6,
dynIdxEnd: 36,
}
prsr := &RSRParser{
Rules: "~*req.<~*req.CGRID;~*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 := utils.MapStorage{
utils.MetaReq: utils.MapStorage{
utils.CGRID: "cgridUniq",
utils.RunID: utils.MetaDefault,
"cgridUniq*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)
}
}

View File

@@ -147,21 +147,6 @@ func (rsrf *RSRField) Compile() error {
return nil
}
// IsStatic detects if a RSRField is a static value
func (rsrf *RSRField) IsStatic() bool {
return len(rsrf.staticValue) != 0
}
// RegexpMatched will investigate whether we had a regexp match through the rules
func (rsrf *RSRField) RegexpMatched() bool {
for _, rsrule := range rsrf.RSRules {
if rsrule.Matched {
return true
}
}
return false
}
// parseValue the field value from a string
func (rsrf *RSRField) parseValue(value string) string {
if len(rsrf.staticValue) != 0 { // Enforce parsing of static values

View File

@@ -214,17 +214,6 @@ func TestPrefix164(t *testing.T) {
}
}
func TestIsStatic(t *testing.T) {
rsr1 := &RSRField{Id: "0", staticValue: "0"}
if !rsr1.IsStatic() {
t.Error("Failed to detect static value.")
}
rsr2 := &RSRField{Id: "0", RSRules: []*ReSearchReplace{{SearchRegexp: regexp.MustCompile(`^([1-9]\d+)$`), ReplaceTemplate: "+$1"}}}
if rsr2.IsStatic() {
t.Error("Non static detected as static value")
}
}
func TestParseRSRFields(t *testing.T) {
fieldsStr1 := `~account:s/^\w+[mpls]\d{6}$//;~subject:s/^0\d{9}$//;^destination/+4912345/;~mediation_runid:s/^default$/default/`
rsrFld1, _ := NewRSRField(`~account:s/^\w+[mpls]\d{6}$//`)