From f9454bfb4c3eceaf47e23c2b45a7809358de04f0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Jun 2020 17:29:55 +0300 Subject: [PATCH] Added DynRSRParser functionality --- config/rsrparser.go | 104 +++++++++++++++++++++++++++++++++++++++ config/rsrparser_test.go | 30 +++++++++++ utils/rsrfield.go | 15 ------ utils/rsrfield_test.go | 11 ----- 4 files changed, 134 insertions(+), 26 deletions(-) diff --git a/config/rsrparser.go b/config/rsrparser.go index 5bc52968d..e64b3d9f1 100644 --- a/config/rsrparser.go +++ b/config/rsrparser.go @@ -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 +} diff --git a/config/rsrparser_test.go b/config/rsrparser_test.go index 3abb5e0cd..a07d4254d 100644 --- a/config/rsrparser_test.go +++ b/config/rsrparser_test.go @@ -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) + } +} diff --git a/utils/rsrfield.go b/utils/rsrfield.go index 5b806b705..95b9b4f0f 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -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 diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index 5d7453c36..40f0c1678 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -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}$//`)