diff --git a/agents/agentreq.go b/agents/agentreq.go index 2b66704d8..202ec4f17 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -368,18 +368,18 @@ func (ar *AgentRequest) ParseField( out = ar.RemoteHost().String() isString = true case utils.MetaVariable, utils.META_COMPOSED, utils.MetaGroup: - out, err = cfgFld.Value.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + out, err = cfgFld.Value.ParseDataProvider(ar.dynamicProvider) isString = true case utils.META_USAGE_DIFFERENCE: if len(cfgFld.Value) != 2 { return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.META_USAGE_DIFFERENCE) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } - strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } @@ -398,7 +398,7 @@ func (ar *AgentRequest) ParseField( return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.MetaCCUsage) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) // ReqNr + strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider) // ReqNr if err != nil { return "", err } @@ -407,7 +407,7 @@ func (ar *AgentRequest) ParseField( return "", fmt.Errorf("invalid requestNumber <%s> to %s", strVal1, utils.MetaCCUsage) } - strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) // TotalUsage + strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider) // TotalUsage if err != nil { return "", err } @@ -416,7 +416,7 @@ func (ar *AgentRequest) ParseField( return "", fmt.Errorf("invalid usedCCTime <%s> to %s", strVal2, utils.MetaCCUsage) } - strVal3, err := cfgFld.Value[2].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) // DebitInterval + strVal3, err := cfgFld.Value[2].ParseDataProvider(ar.dynamicProvider) // DebitInterval if err != nil { return "", err } @@ -433,7 +433,7 @@ func (ar *AgentRequest) ParseField( case utils.MetaSum: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } @@ -443,7 +443,7 @@ func (ar *AgentRequest) ParseField( case utils.MetaDifference: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } @@ -453,7 +453,7 @@ func (ar *AgentRequest) ParseField( case utils.MetaMultiply: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } @@ -463,7 +463,7 @@ func (ar *AgentRequest) ParseField( case utils.MetaDivide: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(ar.dynamicProvider) if err != nil { return "", err } @@ -475,7 +475,7 @@ func (ar *AgentRequest) ParseField( return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.MetaValueExponent) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) // String Value + strVal1, err := cfgFld.Value[0].ParseDataProvider(ar.dynamicProvider) // String Value if err != nil { return "", err } @@ -484,7 +484,7 @@ func (ar *AgentRequest) ParseField( return "", fmt.Errorf("invalid value <%s> to %s", strVal1, utils.MetaValueExponent) } - strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider, utils.NestingSep) // String Exponent + strVal2, err := cfgFld.Value[1].ParseDataProvider(ar.dynamicProvider) // String Exponent if err != nil { return "", err } @@ -495,7 +495,7 @@ func (ar *AgentRequest) ParseField( out = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), config.CgrConfig().GeneralCfg().RoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64) case utils.MetaUnixTimestamp: - val, err := cfgFld.Value.ParseDataProvider(ar.dynamicProvider, utils.NestingSep) + val, err := cfgFld.Value.ParseDataProvider(ar.dynamicProvider) if err != nil { return nil, err } diff --git a/agents/fsevent_test.go b/agents/fsevent_test.go index 1eb88a71a..901db9ac3 100644 --- a/agents/fsevent_test.go +++ b/agents/fsevent_test.go @@ -491,60 +491,60 @@ func TestParseEventValue(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) ev := NewFSEvent(hangupEv) - if tor, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.ToR, true), ""); tor != utils.VOICE { - t.Error("Unexpected tor parsed", tor) + if tor, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.ToR, true), ""); tor != utils.VOICE { + t.Errorf("Unexpected tor parsed %q", tor) } - if accid, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.OriginID, true), ""); accid != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { + if accid, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.OriginID, true), ""); accid != "e3133bf7-dcde-4daf-9663-9a79ffcef5ad" { t.Error("Unexpected result parsed", accid) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.OriginHost, true), ""); parsed != "10.0.3.15" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.OriginHost, true), ""); parsed != "10.0.3.15" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Source, true), ""); parsed != "FS_EVENT" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Source, true), ""); parsed != "FS_EVENT" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.RequestType, true), ""); parsed != utils.META_PREPAID { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.RequestType, true), ""); parsed != utils.META_PREPAID { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Tenant, true), ""); parsed != "cgrates.org" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Tenant, true), ""); parsed != "cgrates.org" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Category, true), ""); parsed != "call" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Category, true), ""); parsed != "call" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Account, true), ""); parsed != "1001" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Account, true), ""); parsed != "1001" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Subject, true), ""); parsed != "1001" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Subject, true), ""); parsed != "1001" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Destination, true), ""); parsed != "1003" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Destination, true), ""); parsed != "1003" { t.Error("Unexpected result parsed", parsed) } sTime, _ := utils.ParseTimeDetectLayout("1436280728471153"[:len("1436280728471153")-6], "") // We discard nanoseconds information so we can correlate csv - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.SetupTime, true), ""); parsed != sTime.String() { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.SetupTime, true), ""); parsed != sTime.String() { t.Errorf("Expecting: %s, parsed: %s", sTime.String(), parsed) } aTime, _ := utils.ParseTimeDetectLayout("1436280728971147"[:len("1436280728971147")-6], "") - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.AnswerTime, true), ""); parsed != aTime.String() { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.AnswerTime, true), ""); parsed != aTime.String() { t.Errorf("Expecting: %s, parsed: %s", aTime.String(), parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.Usage, true), ""); parsed != "66000000000" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Usage, true), ""); parsed != "66000000000" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.PDD, true), ""); parsed != "0.028" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.PDD, true), ""); parsed != "0.028" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.SUPPLIER, true), ""); parsed != "supplier1" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.SUPPLIER, true), ""); parsed != "supplier1" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.RunID, true), ""); parsed != utils.MetaDefault { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.RunID, true), ""); parsed != utils.MetaDefault { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+utils.COST, true), ""); parsed != "-1" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.COST, true), ""); parsed != "-1" { t.Error("Unexpected result parsed", parsed) } - if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.REGEXP_PREFIX+"Hangup-Cause", true), ""); parsed != "NORMAL_CLEARING" { + if parsed, _ := ev.ParseEventValue(config.NewRSRParserMustCompile(utils.DynamicDataPrefix+"Hangup-Cause", true), ""); parsed != "NORMAL_CLEARING" { t.Error("Unexpected result parsed", parsed) } } diff --git a/config/rsrparser.go b/config/rsrparser.go index 0073ca6e0..6706121d7 100644 --- a/config/rsrparser.go +++ b/config/rsrparser.go @@ -35,16 +35,51 @@ func NewRSRParsers(parsersRules string, allFiltersMatch bool, rsrSeparator strin if parsersRules == "" { return } - if strings.HasPrefix(parsersRules, utils.META_CONSTANT+utils.InInFieldSep) { // in case we do not want the rule to be processed at all - // for example to compose a field in the agent request that contains `=` - prsrs = RSRParsers{ - { - Rules: parsersRules, - AllFiltersMatch: allFiltersMatch, - }, + if count := strings.Count(parsersRules, "`"); count%2 != 0 { // check if we have matching ` + return nil, fmt.Errorf("Unclosed unspilit syntax") + } else if count != 0 { + var splitedRule []string + for idx := strings.IndexByte(parsersRules, '`'); idx != -1; idx = strings.IndexByte(parsersRules, '`') { + insideARulePrefix := !strings.HasSuffix(parsersRules[:idx], utils.INFIELD_SEP) // if doesn't have ; we need to concatenate it with last rule + if insideARulePrefix { + splitedRule = append(splitedRule, strings.Split(parsersRules[:idx], utils.INFIELD_SEP)...) + } else { + splitedRule = append(splitedRule, strings.Split(parsersRules[:idx-1], utils.INFIELD_SEP)...) + } + parsersRules = parsersRules[idx+1:] + idx = strings.IndexByte(parsersRules, '`') + 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, utils.INFIELD_SEP) // if doesn't have ; we need to concatenate it with last rule + if insideARuleSufix { + idx = strings.IndexByte(parsersRules, ';') + 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, utils.INFIELD_SEP)...) + break + } } - err = prsrs.Compile() - return + return NewRSRParsersFromSlice(splitedRule, allFiltersMatch) } return NewRSRParsersFromSlice(strings.Split(parsersRules, rsrSeparator), allFiltersMatch) } @@ -75,12 +110,11 @@ func NewRSRParsersMustCompile(parsersRules string, allFiltersMatch bool, rsrSepa type RSRParsers []*RSRParser func (prsrs RSRParsers) GetRule() (out string) { - for i, prsr := range prsrs { - if i == 0 { - out = prsr.Rules - } else { - out = out + utils.NestingSep + prsr.Rules - } + for _, prsr := range prsrs { + out += utils.INFIELD_SEP + prsr.Rules + } + if len(out) != 0 { + out = out[1:] } return } @@ -106,36 +140,24 @@ func (prsrs RSRParsers) ParseValue(value interface{}) (out string, err error) { return } -// ParseEvent will parse the event values into one output -func (prsrs RSRParsers) ParseEvent(ev map[string]interface{}) (out string, err error) { +func (prsrs RSRParsers) ParseDataProvider(dP utils.DataProvider) (out string, err error) { for _, prsr := range prsrs { - if outPrsr, err := prsr.ParseEvent(ev); err != nil { + var outPrsr string + if outPrsr, err = prsr.ParseDataProvider(dP); err != nil { return "", err - } else { - out += outPrsr } + out += outPrsr } return } -func (prsrs RSRParsers) ParseDataProvider(dP utils.DataProvider, separator string) (out string, err error) { +func (prsrs RSRParsers) ParseDataProviderWithInterfaces(dP utils.DataProvider) (out string, err error) { for _, prsr := range prsrs { - if outPrsr, err := prsr.ParseDataProvider(dP, separator); err != nil { + var outPrsr string + if outPrsr, err = prsr.ParseDataProviderWithInterfaces(dP); err != nil { return "", err - } else { - out += outPrsr - } - } - return -} - -func (prsrs RSRParsers) ParseDataProviderWithInterfaces(dP utils.DataProvider, separator string) (out string, err error) { - for _, prsr := range prsrs { - if outPrsr, err := prsr.ParseDataProviderWithInterfaces(dP, separator); err != nil { - return "", err - } else { - out += outPrsr } + out += outPrsr } return } @@ -167,46 +189,38 @@ func NewRSRParser(parserRules string, allFiltersMatch bool) (rsrParser *RSRParse convsSplt := strings.Split(convertersStr, utils.ANDSep) rsrParser.converters = make(utils.DataConverters, len(convsSplt)) for i, convStr := range convsSplt { - if conv, err := utils.NewDataConverter(convStr); err != nil { + var conv utils.DataConverter + if conv, err = utils.NewDataConverter(convStr); err != nil { return nil, fmt.Errorf("invalid converter value in string: <%s>, err: %s", convStr, err.Error()) - } else { - rsrParser.converters[i] = conv } + rsrParser.converters[i] = conv } parserRules = parserRules[:idxConverters] } - if !strings.HasPrefix(parserRules, utils.DynamicDataPrefix) || len(parserRules) == 1 { // special case when RSR is defined as static attribute=value - var staticHdr, staticVal string - if splt := strings.Split(parserRules, utils.AttrValueSep); len(splt) == 2 { // using '='' as separator since ':' is often use in date/time fields - staticHdr, staticVal = splt[0], splt[1] // strip the separator - if strings.HasSuffix(staticVal, utils.AttrValueSep) { // if value ends with sep, strip it since it is a part of the definition syntax - staticVal = staticVal[:len(staticVal)-1] - } - } else if len(splt) > 2 { - return nil, fmt.Errorf("invalid RSRField static rules: <%s>", parserRules) - } else { - staticVal = splt[0] // no attribute name - } - rsrParser.attrName = staticHdr - rsrParser.attrValue = staticVal + if !strings.HasPrefix(parserRules, utils.DynamicDataPrefix) || + len(parserRules) == 1 { // special case when RSR is defined as static attribute + rsrParser.path = parserRules return } // dynamic content via attributeNames spltRules := spltRgxp.Split(parserRules, -1) - rsrParser.attrName = spltRules[0][1:] // in form ~hdr_name + rsrParser.path = spltRules[0] // in form ~hdr_name if len(spltRules) > 1 { for _, ruleStr := range spltRules[1:] { // :s/ already removed through split allMatches := rulesRgxp.FindStringSubmatch(ruleStr) if len(allMatches) != 3 { return nil, fmt.Errorf("not enough members in Search&Replace, ruleStr: <%s>, matches: %v, ", ruleStr, allMatches) } - if srRegexp, err := regexp.Compile(allMatches[1]); err != nil { + var srRegexp *regexp.Regexp + if srRegexp, err = regexp.Compile(allMatches[1]); err != nil { return nil, fmt.Errorf("invalid Search&Replace subfield rule: <%s>", allMatches[1]) - } else { - rsrParser.rsrRules = append(rsrParser.rsrRules, &utils.ReSearchReplace{SearchRegexp: srRegexp, ReplaceTemplate: allMatches[2]}) } + rsrParser.rsrRules = append(rsrParser.rsrRules, &utils.ReSearchReplace{ + SearchRegexp: srRegexp, + ReplaceTemplate: allMatches[2], + }) } } return @@ -225,8 +239,7 @@ type RSRParser struct { Rules string // Rules container holding the string rules, public so it can be stored AllFiltersMatch bool // all filters must match policy - attrName string // instruct extracting info out of header in event - attrValue string // if populated, enforces parsing always to this value + 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 filters utils.RSRFilters // The value to compare when used as filter @@ -234,16 +247,12 @@ type RSRParser struct { // AttrName exports the attribute name of the RSRParser func (prsr *RSRParser) AttrName() string { - return prsr.attrName + return strings.TrimPrefix(prsr.path, utils.DynamicDataPrefix) } // Compile parses Rules string and repopulates other fields func (prsr *RSRParser) Compile() (err error) { var newPrsr *RSRParser - if strings.HasPrefix(prsr.Rules, utils.META_CONSTANT+utils.InInFieldSep) { // in case we do not want the rule to be processed at all - prsr.attrValue = strings.TrimPrefix(prsr.Rules, utils.META_CONSTANT+utils.InInFieldSep) - return - } if newPrsr, err = NewRSRParser(prsr.Rules, prsr.AllFiltersMatch); err != nil { return } @@ -264,20 +273,12 @@ func (prsr *RSRParser) RegexpMatched() bool { } // parseValue the field value from a string -func (prsr *RSRParser) parseValue(value string) string { - if prsr.attrValue != "" { // Enforce parsing of static values - return prsr.attrValue - } +func (prsr *RSRParser) parseValue(value string) (out string, err error) { for _, rsRule := range prsr.rsrRules { value = rsRule.Process(value) } - return value -} -// ParseValue will parse the value out considering converters and filters -func (prsr *RSRParser) ParseValue(value interface{}) (out string, err error) { - out = prsr.parseValue(utils.IfaceAsString(value)) - if out, err = prsr.converters.ConvertString(out); err != nil { + if out, err = prsr.converters.ConvertString(value); err != nil { return } if !prsr.filters.Pass(out, prsr.AllFiltersMatch) { @@ -286,35 +287,30 @@ func (prsr *RSRParser) ParseValue(value interface{}) (out string, err error) { return } -// ParseEvent will parse the value out considering converters and filters -func (prsr *RSRParser) ParseEvent(ev map[string]interface{}) (out string, err error) { - val, has := ev[prsr.attrName] - if !has && prsr.attrValue == "" { - return "", utils.ErrNotFound +// ParseValue will parse the value out considering converters and filters +func (prsr *RSRParser) ParseValue(value interface{}) (out string, err error) { + out = prsr.path + if out != utils.DynamicDataPrefix && + strings.HasPrefix(out, utils.DynamicDataPrefix) { // Enforce parsing of static values + out = utils.IfaceAsString(value) } - return prsr.ParseValue(val) + return prsr.parseValue(out) } -func (prsr *RSRParser) ParseDataProvider(dP utils.DataProvider, separator string) (out string, err error) { +func (prsr *RSRParser) ParseDataProvider(dP utils.DataProvider) (out string, err error) { var outStr string - if prsr.attrValue == "" { - if outStr, err = dP.FieldAsString( - strings.Split(prsr.attrName, separator)); err != nil && - (err != utils.ErrNotFound || prsr.filters.FilterRules() != "^$") { - return - } + if outStr, err = utils.DPDynamicString(prsr.path, dP); err != nil && + (err != utils.ErrNotFound || prsr.filters.FilterRules() != "^$") { + return } - return prsr.ParseValue(outStr) + return prsr.parseValue(outStr) } -func (prsr *RSRParser) ParseDataProviderWithInterfaces(dP utils.DataProvider, separator string) (out string, err error) { +func (prsr *RSRParser) ParseDataProviderWithInterfaces(dP utils.DataProvider) (out string, err error) { var outIface interface{} - if prsr.attrValue == "" { - if outIface, err = dP.FieldAsInterface( - strings.Split(prsr.attrName, separator)); err != nil && - (err != utils.ErrNotFound || prsr.filters.FilterRules() != "^$") { - return - } + if outIface, err = utils.DPDynamicInterface(prsr.path, dP); err != nil && + (err != utils.ErrNotFound || prsr.filters.FilterRules() != "^$") { + return } - return prsr.ParseValue(outIface) + return prsr.parseValue(utils.IfaceAsString(outIface)) } diff --git a/config/rsrparser_test.go b/config/rsrparser_test.go index 5d93d3275..007bd657a 100644 --- a/config/rsrparser_test.go +++ b/config/rsrparser_test.go @@ -27,16 +27,16 @@ import ( ) func TestNewRSRParsers(t *testing.T) { - ruleStr := `Value1;Heade2=Value2;~Header3(Val3&!Val4);~Header4:s/a/${1}b/{*duration_seconds&*round:2}(b&c);Value5{*duration_seconds&*round:2}` + ruleStr := `Value1;Value2;~Header3(Val3&!Val4);~Header4:s/a/${1}b/{*duration_seconds&*round:2}(b&c);Value5{*duration_seconds&*round:2}` eRSRParsers := RSRParsers{ - &RSRParser{Rules: "Value1", AllFiltersMatch: true, attrValue: "Value1"}, - &RSRParser{Rules: "Heade2=Value2", AllFiltersMatch: true, attrName: "Heade2", attrValue: "Value2"}, - &RSRParser{Rules: "~Header3(Val3&!Val4)", AllFiltersMatch: true, attrName: "Header3", + &RSRParser{Rules: "Value1", AllFiltersMatch: true, path: "Value1"}, + &RSRParser{Rules: "Value2", AllFiltersMatch: true, path: "Value2"}, + &RSRParser{Rules: "~Header3(Val3&!Val4)", AllFiltersMatch: true, path: "~Header3", filters: utils.RSRFilters{utils.NewRSRFilterMustCompile("Val3"), utils.NewRSRFilterMustCompile("!Val4")}}, &RSRParser{Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}(b&c)", AllFiltersMatch: true, - attrName: "Header4", + path: "~Header4", rsrRules: []*utils.ReSearchReplace{{ SearchRegexp: regexp.MustCompile(`a`), ReplaceTemplate: "${1}b"}}, @@ -47,7 +47,7 @@ func TestNewRSRParsers(t *testing.T) { }, &RSRParser{Rules: "Value5{*duration_seconds&*round:2}", AllFiltersMatch: true, - attrValue: "Value5", + path: "Value5", converters: utils.DataConverters{utils.NewDataConverterMustCompile("*duration_seconds"), utils.NewDataConverterMustCompile("*round:2")}, }, @@ -61,8 +61,8 @@ func TestNewRSRParsers(t *testing.T) { func TestRSRParserCompile(t *testing.T) { ePrsr := &RSRParser{ - Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}(b&c)", - attrName: "Header4", + Rules: "~Header4:s/a/${1}b/{*duration_seconds&*round:2}(b&c)", + path: "~Header4", rsrRules: []*utils.ReSearchReplace{{ SearchRegexp: regexp.MustCompile(`a`), ReplaceTemplate: "${1}b"}}, @@ -81,20 +81,6 @@ func TestRSRParserCompile(t *testing.T) { } } -func TestRSRParsersParseEvent(t *testing.T) { - prsrs := NewRSRParsersMustCompile("~Header1;|;~Header2", true, utils.INFIELD_SEP) - ev := map[string]interface{}{ - "Header1": "Value1", - "Header2": "Value2", - } - eOut := "Value1|Value2" - if out, err := prsrs.ParseEvent(ev); err != nil { - t.Error(err) - } else if eOut != out { - t.Errorf("expecting: %s, received: %s", eOut, out) - } -} - func TestRSRParserConstant(t *testing.T) { rule := "cgrates.org" rsrParsers, err := NewRSRParsers(rule, true, utils.INFIELD_SEP) @@ -121,36 +107,6 @@ func TestRSRParserNotConstant(t *testing.T) { } } -func TestRSRParsersParseEvent2(t *testing.T) { - prsrs := NewRSRParsersMustCompile("~Header1.Test;|;~Header2.Test", true, utils.INFIELD_SEP) - ev := map[string]interface{}{ - "Header1.Test": "Value1", - "Header2.Test": "Value2", - } - eOut := "Value1|Value2" - if out, err := prsrs.ParseEvent(ev); err != nil { - t.Error(err) - } else if eOut != out { - t.Errorf("expecting: %s, received: %s", eOut, out) - } -} - -func TestRSRParsersParseEvent3(t *testing.T) { - prsr, err := NewRSRParser("~Category:s/(.*)/${1}_suffix/", true) - if err != nil { - t.Error(err) - } - ev := map[string]interface{}{ - "Category": "call", - } - eOut := "call_suffix" - if out, err := prsr.ParseEvent(ev); err != nil { - t.Error(err) - } else if eOut != out { - t.Errorf("expecting: %s, received: %s", eOut, out) - } -} - // TestRSRParsersParseInnerBraces makes sure the inner braces are allowed in a filter rule func TestRSRParsersParseInnerBracket(t *testing.T) { rule := "~*req.Service-Information.IN-Information.CalledPartyAddress(~^(00)*(33|0)890240004$)" @@ -165,25 +121,58 @@ func TestRSRParsersParseInnerBracket(t *testing.T) { } func TestNewRSRParsersConstant(t *testing.T) { - ruleStr := `*constant:>;q=0.7;expires=3600` + ruleStr := "`>;q=0.7;expires=3600`" eRSRParsers := RSRParsers{ - &RSRParser{Rules: "*constant:>;q=0.7;expires=3600", AllFiltersMatch: true, attrValue: ">;q=0.7;expires=3600"}, + &RSRParser{Rules: ">;q=0.7;expires=3600", AllFiltersMatch: true, path: ">;q=0.7;expires=3600"}, } if rsrParsers, err := NewRSRParsers(ruleStr, true, utils.INFIELD_SEP); 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(utils.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, true, utils.INFIELD_SEP); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if out, err := rsrParsers.ParseDataProvider(utils.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, true, utils.INFIELD_SEP); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if out, err := rsrParsers.ParseDataProvider(utils.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, true, utils.INFIELD_SEP); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if out, err := rsrParsers.ParseDataProvider(utils.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) } } func TestRSRParserCompileConstant(t *testing.T) { ePrsr := &RSRParser{ - Rules: "*constant:>;q=0.7;expires=3600", + Rules: ":>;q=0.7;expires=3600", AllFiltersMatch: true, - attrValue: ">;q=0.7;expires=3600", + path: ":>;q=0.7;expires=3600", } prsr := &RSRParser{ - Rules: "*constant:>;q=0.7;expires=3600", + Rules: ":>;q=0.7;expires=3600", AllFiltersMatch: true, } if err := prsr.Compile(); err != nil { @@ -192,3 +181,17 @@ func TestRSRParserCompileConstant(t *testing.T) { t.Errorf("expecting: %+v, received: %+v", ePrsr, prsr) } } + +func TestNewRSRParsersParseDataProviderWithInterfaces(t *testing.T) { + ruleStr := "~;*accounts.;~*req.Account" + if rsrParsers, err := NewRSRParsers(ruleStr, true, utils.INFIELD_SEP); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if out, err := rsrParsers.ParseDataProviderWithInterfaces( + utils.MapStorage{ + utils.MetaReq: utils.MapStorage{utils.Account: "1001"}, + }); err != nil { + t.Error(err) + } else if expected := "~*accounts.1001"; out != expected { + t.Errorf("Expected %q ,received %q", expected, out) + } +} diff --git a/data/conf/samples/sipagent_internal/redirect.json b/data/conf/samples/sipagent_internal/redirect.json index a87976410..f5a786e94 100644 --- a/data/conf/samples/sipagent_internal/redirect.json +++ b/data/conf/samples/sipagent_internal/redirect.json @@ -51,11 +51,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[0].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.7; expires=3600;cgr_cost="}, + "value":"`>;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.MaxUsage"} ] @@ -80,11 +80,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[1].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.2;cgr_cost="}, + "value":"`>;q=0.2;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.MaxUsage"} ] @@ -109,11 +109,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[2].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.1;cgr_cost="}, + "value":"`>;q=0.1;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.MaxUsage"} ] diff --git a/data/conf/samples/sipagent_mongo/redirect.json b/data/conf/samples/sipagent_mongo/redirect.json index a87976410..f5a786e94 100644 --- a/data/conf/samples/sipagent_mongo/redirect.json +++ b/data/conf/samples/sipagent_mongo/redirect.json @@ -51,11 +51,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[0].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.7; expires=3600;cgr_cost="}, + "value":"`>;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.MaxUsage"} ] @@ -80,11 +80,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[1].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.2;cgr_cost="}, + "value":"`>;q=0.2;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.MaxUsage"} ] @@ -109,11 +109,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[2].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.1;cgr_cost="}, + "value":"`>;q=0.1;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.MaxUsage"} ] diff --git a/data/conf/samples/sipagent_mysql/redirect.json b/data/conf/samples/sipagent_mysql/redirect.json index a87976410..f5a786e94 100644 --- a/data/conf/samples/sipagent_mysql/redirect.json +++ b/data/conf/samples/sipagent_mysql/redirect.json @@ -51,11 +51,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[0].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.7; expires=3600;cgr_cost="}, + "value":"`>;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.MaxUsage"} ] @@ -80,11 +80,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[1].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.2;cgr_cost="}, + "value":"`>;q=0.2;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.MaxUsage"} ] @@ -109,11 +109,11 @@ {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value": "~*cgrep.Routes.SortedRoutes[2].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>;q=0.1;cgr_cost="}, + "value":"`>;q=0.1;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.MaxUsage"} ] diff --git a/data/tutorials/sip_redirect/cgrates/etc/cgrates/redirect.json b/data/tutorials/sip_redirect/cgrates/etc/cgrates/redirect.json index 0a6d1c075..b1b832ca2 100644 --- a/data/tutorials/sip_redirect/cgrates/etc/cgrates/redirect.json +++ b/data/tutorials/sip_redirect/cgrates/etc/cgrates/redirect.json @@ -51,22 +51,22 @@ "value": "~*cgrep.Routes.SortedRoutes[0].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;q=0.7; expires=3600;cgr_cost="}, + "value":"`;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].SortingData.MaxUsage"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_route="}, + "value":"`;cgr_route=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[0].RouteID"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>"}, + "value":"`>`"}, {"tag": "X-Identity", "path": "*rep.X-Identity", "type": "*variable", "value":"~*cgrep.STIRIdentity[~*cgrep.Routes.SortedRoutes[0].RouteID]"}, @@ -86,22 +86,22 @@ "value": "~*cgrep.Routes.SortedRoutes[1].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;q=0.7; expires=3600;cgr_cost="}, + "value":"`;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].SortingData.MaxUsage"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_route="}, + "value":"`;cgr_route=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[1].RouteID"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>"}, + "value":"`>`"}, ] }, { @@ -117,22 +117,22 @@ "value": "~*cgrep.Routes.SortedRoutes[2].RouteParameters"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;q=0.7; expires=3600;cgr_cost="}, + "value":"`;q=0.7; expires=3600;cgr_cost=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.Cost"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_maxusage="}, + "value":"`;cgr_maxusage=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].SortingData.MaxUsage"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:;cgr_route="}, + "value":"`;cgr_route=`"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", "value":"~*cgrep.Routes.SortedRoutes[2].RouteID"}, {"tag": "Contact", "path": "*rep.Contact", "type": "*composed", - "value":"*constant:>"}, + "value":"`>`"}, ] }, ] diff --git a/ees/eereq.go b/ees/eereq.go index 4441fc363..3a8a95689 100644 --- a/ees/eereq.go +++ b/ees/eereq.go @@ -222,18 +222,18 @@ func (eeR *EventExporterRequest) ParseField( out = eeR.RemoteHost().String() isString = true case utils.MetaVariable, utils.META_COMPOSED, utils.MetaGroup: - out, err = cfgFld.Value.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + out, err = cfgFld.Value.ParseDataProvider(eeR.dynamicProvider) isString = true case utils.META_USAGE_DIFFERENCE: if len(cfgFld.Value) != 2 { return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.META_USAGE_DIFFERENCE) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } - strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } @@ -252,7 +252,7 @@ func (eeR *EventExporterRequest) ParseField( return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.MetaCCUsage) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) // ReqNr + strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider) // ReqNr if err != nil { return "", err } @@ -261,7 +261,7 @@ func (eeR *EventExporterRequest) ParseField( return "", fmt.Errorf("invalid requestNumber <%s> to %s", strVal1, utils.MetaCCUsage) } - strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) // TotalUsage + strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider) // TotalUsage if err != nil { return "", err } @@ -270,7 +270,7 @@ func (eeR *EventExporterRequest) ParseField( return "", fmt.Errorf("invalid usedCCTime <%s> to %s", strVal2, utils.MetaCCUsage) } - strVal3, err := cfgFld.Value[2].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) // DebitInterval + strVal3, err := cfgFld.Value[2].ParseDataProvider(eeR.dynamicProvider) // DebitInterval if err != nil { return "", err } @@ -287,7 +287,7 @@ func (eeR *EventExporterRequest) ParseField( case utils.MetaSum: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } @@ -297,7 +297,7 @@ func (eeR *EventExporterRequest) ParseField( case utils.MetaDifference: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } @@ -307,7 +307,7 @@ func (eeR *EventExporterRequest) ParseField( case utils.MetaMultiply: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } @@ -317,7 +317,7 @@ func (eeR *EventExporterRequest) ParseField( case utils.MetaDivide: iFaceVals := make([]interface{}, len(cfgFld.Value)) for i, val := range cfgFld.Value { - strVal, err := val.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + strVal, err := val.ParseDataProvider(eeR.dynamicProvider) if err != nil { return "", err } @@ -329,7 +329,7 @@ func (eeR *EventExporterRequest) ParseField( return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(cfgFld.Value), utils.MetaValueExponent) } - strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) // String Value + strVal1, err := cfgFld.Value[0].ParseDataProvider(eeR.dynamicProvider) // String Value if err != nil { return "", err } @@ -338,7 +338,7 @@ func (eeR *EventExporterRequest) ParseField( return "", fmt.Errorf("invalid value <%s> to %s", strVal1, utils.MetaValueExponent) } - strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) // String Exponent + strVal2, err := cfgFld.Value[1].ParseDataProvider(eeR.dynamicProvider) // String Exponent if err != nil { return "", err } @@ -349,7 +349,7 @@ func (eeR *EventExporterRequest) ParseField( out = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), config.CgrConfig().GeneralCfg().RoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64) case utils.MetaUnixTimestamp: - val, err := cfgFld.Value.ParseDataProvider(eeR.dynamicProvider, utils.NestingSep) + val, err := cfgFld.Value.ParseDataProvider(eeR.dynamicProvider) if err != nil { return nil, err } diff --git a/ees/ees.go b/ees/ees.go index 6b7494922..75267e6bc 100644 --- a/ees/ees.go +++ b/ees/ees.go @@ -142,7 +142,7 @@ func (eeS *EventExporterS) V1ProcessEvent(cgrEv *utils.CGREventWithOpts, rply *s if len(eeCfg.Filters) != 0 { cgrDp := utils.MapStorage{utils.MetaReq: cgrEv.Event} tnt := cgrEv.Tenant - if eeTnt, errTnt := eeCfg.Tenant.ParseEvent(cgrEv.Event); errTnt == nil && eeTnt != utils.EmptyString { + if eeTnt, errTnt := eeCfg.Tenant.ParseDataProvider(cgrDp); errTnt == nil && eeTnt != utils.EmptyString { tnt = eeTnt } if pass, errPass := eeS.filterS.Pass(tnt, diff --git a/engine/action.go b/engine/action.go index 98e4a5ace..6c74b7701 100644 --- a/engine/action.go +++ b/engine/action.go @@ -180,7 +180,7 @@ func cdrLogAction(acc *Account, a *Action, acs Actions, extraData interface{}) ( cdr.CGRID = utils.Sha1(cdr.OriginID, cdr.OriginHost) elem := reflect.ValueOf(cdr).Elem() for key, rsrFlds := range defaultTemplate { - parsedValue, err := rsrFlds.ParseDataProvider(cdrLogProvider, utils.NestingSep) + parsedValue, err := rsrFlds.ParseDataProvider(cdrLogProvider) if err != nil { return err } diff --git a/engine/attributes.go b/engine/attributes.go index 272a7e1b4..415e6ac4d 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -197,16 +197,16 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( case utils.META_CONSTANT: substitute, err = attribute.Value.ParseValue(utils.EmptyString) case utils.MetaVariable, utils.META_COMPOSED: - substitute, err = attribute.Value.ParseDataProvider(evNm, utils.NestingSep) + substitute, err = attribute.Value.ParseDataProvider(evNm) case utils.META_USAGE_DIFFERENCE: if len(attribute.Value) != 2 { return nil, fmt.Errorf("invalid arguments <%s>", utils.ToJSON(attribute.Value)) } - strVal1, err := attribute.Value[0].ParseDataProvider(evNm, utils.NestingSep) + strVal1, err := attribute.Value[0].ParseDataProvider(evNm) if err != nil { return nil, err } - strVal2, err := attribute.Value[1].ParseDataProvider(evNm, utils.NestingSep) + strVal2, err := attribute.Value[1].ParseDataProvider(evNm) if err != nil { return nil, err } @@ -222,7 +222,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( case utils.MetaSum: iFaceVals := make([]interface{}, len(attribute.Value)) for i, val := range attribute.Value { - strVal, err := val.ParseDataProvider(evNm, utils.NestingSep) + strVal, err := val.ParseDataProvider(evNm) if err != nil { return nil, err } @@ -236,7 +236,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( case utils.MetaDifference: iFaceVals := make([]interface{}, len(attribute.Value)) for i, val := range attribute.Value { - strVal, err := val.ParseDataProvider(evNm, utils.NestingSep) + strVal, err := val.ParseDataProvider(evNm) if err != nil { return nil, err } @@ -250,7 +250,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( case utils.MetaMultiply: iFaceVals := make([]interface{}, len(attribute.Value)) for i, val := range attribute.Value { - strVal, err := val.ParseDataProvider(evNm, utils.NestingSep) + strVal, err := val.ParseDataProvider(evNm) if err != nil { return nil, err } @@ -264,7 +264,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( case utils.MetaDivide: iFaceVals := make([]interface{}, len(attribute.Value)) for i, val := range attribute.Value { - strVal, err := val.ParseDataProvider(evNm, utils.NestingSep) + strVal, err := val.ParseDataProvider(evNm) if err != nil { return nil, err } @@ -280,7 +280,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( return nil, fmt.Errorf("invalid arguments <%s> to %s", utils.ToJSON(attribute.Value), utils.MetaValueExponent) } - strVal1, err := attribute.Value[0].ParseDataProvider(evNm, utils.NestingSep) // String Value + strVal1, err := attribute.Value[0].ParseDataProvider(evNm) // String Value if err != nil { return nil, err } @@ -289,7 +289,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( return nil, fmt.Errorf("invalid value <%s> to %s", strVal1, utils.MetaValueExponent) } - strVal2, err := attribute.Value[1].ParseDataProvider(evNm, utils.NestingSep) // String Exponent + strVal2, err := attribute.Value[1].ParseDataProvider(evNm) // String Exponent if err != nil { return nil, err } @@ -300,7 +300,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( substitute = strconv.FormatFloat(utils.Round(val*math.Pow10(exp), config.CgrConfig().GeneralCfg().RoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64) case utils.MetaUnixTimestamp: - val, err := attribute.Value.ParseDataProvider(evNm, utils.NestingSep) + val, err := attribute.Value.ParseDataProvider(evNm) if err != nil { return nil, err } @@ -310,7 +310,7 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( } substitute = strconv.Itoa(int(t.Unix())) default: // backwards compatible in case that Type is empty - substitute, err = attribute.Value.ParseDataProvider(evNm, utils.NestingSep) + substitute, err = attribute.Value.ParseDataProvider(evNm) } if err != nil { diff --git a/engine/cdr.go b/engine/cdr.go index 79ff284a2..8c551c655 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -157,7 +157,7 @@ func (cdr *CDR) FieldAsString(rsrPrs *config.RSRParser) (parsed string, err erro utils.MapStorage{ utils.MetaReq: cdr.AsMapStringIface(), utils.MetaEC: cdr.CostDetails, - }, utils.NestingSep) + }) if err != nil { return } @@ -166,11 +166,11 @@ func (cdr *CDR) FieldAsString(rsrPrs *config.RSRParser) (parsed string, err erro // FieldsAsString concatenates values of multiple fields defined in template, used eg in CDR templates func (cdr *CDR) FieldsAsString(rsrFlds config.RSRParsers) string { - outVal, err := rsrFlds.ParseDataProviderWithInterfaces( + outVal, err := rsrFlds.ParseDataProvider( utils.MapStorage{ utils.MetaReq: cdr.AsMapStringIface(), utils.MetaEC: cdr.CostDetails, - }, utils.NestingSep) + }) if err != nil { return "" } diff --git a/engine/cdre.go b/engine/cdre.go index dc3035a0b..86dbe7a14 100644 --- a/engine/cdre.go +++ b/engine/cdre.go @@ -119,21 +119,21 @@ func (cdre *CDRExporter) metaHandler(tag, arg string) (string, error) { return cdre.lastCdrATime.Format(arg), nil case metaNrCDRs: return strconv.Itoa(cdre.numberOfRecords), nil - case metaDurCDRs: + case metaDurCDRs: // ToDo: remove this because they are useless cdr := &CDR{ToR: utils.VOICE, Usage: cdre.totalDuration} - return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) + return cdr.FieldAsString(&config.RSRParser{Rules: utils.DynamicDataPrefix + utils.Usage, AllFiltersMatch: true}) case metaSMSUsage: cdr := &CDR{ToR: utils.SMS, Usage: cdre.totalDuration} - return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) + return cdr.FieldAsString(&config.RSRParser{Rules: utils.DynamicDataPrefix + utils.Usage, AllFiltersMatch: true}) case metaMMSUsage: cdr := &CDR{ToR: utils.MMS, Usage: cdre.totalDuration} - return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) + return cdr.FieldAsString(&config.RSRParser{Rules: utils.DynamicDataPrefix + utils.Usage, AllFiltersMatch: true}) case metaGenericUsage: cdr := &CDR{ToR: utils.GENERIC, Usage: cdre.totalDuration} - return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) + return cdr.FieldAsString(&config.RSRParser{Rules: utils.DynamicDataPrefix + utils.Usage, AllFiltersMatch: true}) case metaDataUsage: cdr := &CDR{ToR: utils.DATA, Usage: cdre.totalDuration} - return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) + return cdr.FieldAsString(&config.RSRParser{Rules: utils.DynamicDataPrefix + utils.Usage, AllFiltersMatch: true}) case metaCostCDRs: return strconv.FormatFloat(utils.Round(cdre.totalCost, globalRoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil diff --git a/engine/cdrefwv_test.go b/engine/cdrefwv_test.go index 2de7d1b0e..50e96ac5b 100644 --- a/engine/cdrefwv_test.go +++ b/engine/cdrefwv_test.go @@ -317,7 +317,7 @@ func TestWriteCdr(t *testing.T) { t.Error(err) } if err = cdre.composeTrailer(); err != nil { - t.Error(err) + t.Fatal(err) } eHeader := "10 VOIfwv_107111308420018011511340001 \n" eContentOut := "201001 1001 1002 0211 07111308420010 1 3dsafdsaf 0002.34570\n" diff --git a/engine/eventcost_test.go b/engine/eventcost_test.go index 7cb9aa473..8555e051b 100644 --- a/engine/eventcost_test.go +++ b/engine/eventcost_test.go @@ -2735,7 +2735,6 @@ func TestEventCostfieldAsInterface(t *testing.T) { if rcv, err := eventCost.fieldAsInterface([]string{utils.Timings, "test1"}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eChargedTiming, rcv) { - fmt.Printf("%T", rcv) t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(eChargedTiming), utils.ToJSON(rcv)) } // case utils.Rates: diff --git a/engine/filters.go b/engine/filters.go index 3ddf84cd1..1f46b7c76 100644 --- a/engine/filters.go +++ b/engine/filters.go @@ -179,6 +179,7 @@ type Filter struct { ActivationInterval *utils.ActivationInterval } +// FilterWithArgDispatcher the arguments for the replication type FilterWithArgDispatcher struct { *Filter *utils.ArgDispatcher @@ -329,7 +330,7 @@ func (fltr *FilterRule) passExists(dDP utils.DataProvider) (bool, error) { var err error path := fltr.Element if fltr.rsrFields != nil { - if path, err = fltr.rsrFields.ParseDataProviderWithInterfaces(dDP, utils.NestingSep); err != nil { + if path, err = fltr.rsrFields.ParseDataProviderWithInterfaces(dDP); err != nil { return false, err } } @@ -446,7 +447,7 @@ func (fltr *FilterRule) passDestinations(dDP utils.DataProvider) (bool, error) { } func (fltr *FilterRule) passRSR(dDP utils.DataProvider) (bool, error) { - _, err := fltr.rsrFields.ParseDataProviderWithInterfaces(dDP, utils.NestingSep) + _, err := fltr.rsrFields.ParseDataProviderWithInterfaces(dDP) if err != nil { if err == utils.ErrNotFound || err == utils.ErrFilterNotPassingNoCaps { return false, nil diff --git a/engine/safevent_test.go b/engine/safevent_test.go index ed1760aae..d83d4d76f 100644 --- a/engine/safevent_test.go +++ b/engine/safevent_test.go @@ -129,7 +129,7 @@ func TestSafEventString(t *testing.T) { for i := 0; i < 10; i++ { t.Run("string", func(t *testing.T) { t.Parallel() - se.String() + _ = se.String() }) t.Run("remove", func(t *testing.T) { t.Parallel() diff --git a/engine/suretax_test.go b/engine/suretax_test.go index a99d4afd4..5972c6cbe 100644 --- a/engine/suretax_test.go +++ b/engine/suretax_test.go @@ -57,7 +57,7 @@ func TestNewSureTaxRequest(t *testing.T) { ResponseGroup: "03", ResponseType: "D4", ItemList: []*STRequestItem{ - &STRequestItem{ + { CustomerNumber: "1001", OrigNumber: "1001", TermNumber: "1002", diff --git a/loaders/libloader.go b/loaders/libloader.go index eebdff810..6c9848cd0 100644 --- a/loaders/libloader.go +++ b/loaders/libloader.go @@ -73,7 +73,7 @@ func (ld LoaderData) UpdateFromCSV(fileName string, record []string, continue // Not passes filters, ignore this CDR } } - out, err := cfgFld.Value.ParseDataProvider(csvProvider, utils.NestingSep) + out, err := cfgFld.Value.ParseDataProvider(csvProvider) if err != nil { return err } diff --git a/packages/debian/changelog b/packages/debian/changelog index 538dcd0b1..4a32e39d7 100644 --- a/packages/debian/changelog +++ b/packages/debian/changelog @@ -73,6 +73,9 @@ cgrates (0.11.0~dev) UNRELEASED; urgency=medium * [LoaderS] Add *req as mandatory prefix * [AgentS] Rename prefix from *cache to *uch * [InternalDB] Updated InternalDB to use the global cache + * [RSRParsers] Removed *constant: prefix + * [RSRParsers] Removed attribute sistem from RSRParser + * [RSRParsers] Added grave accent(`) char as a delimiter to not split tge RSR value -- DanB Wed, 19 Feb 2020 13:25:52 +0200 diff --git a/utils/consts.go b/utils/consts.go index c2a193e23..1fd1068d5 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -187,7 +187,6 @@ const ( FIELDS_SEP = "," InInFieldSep = ":" STATIC_HDRVAL_SEP = "::" - REGEXP_PREFIX = "~" FILTER_VAL_START = "(" FILTER_VAL_END = ")" JSON = "json" diff --git a/utils/dataprovider.go b/utils/dataprovider.go index f0c12cfb9..469b6674e 100644 --- a/utils/dataprovider.go +++ b/utils/dataprovider.go @@ -46,7 +46,8 @@ type NavigableMapper interface { // DPDynamicInterface returns the value of the field if the path is dynamic func DPDynamicInterface(dnVal string, dP DataProvider) (interface{}, error) { - if strings.HasPrefix(dnVal, DynamicDataPrefix) { + if strings.HasPrefix(dnVal, DynamicDataPrefix) && + dnVal != DynamicDataPrefix { dnVal = strings.TrimPrefix(dnVal, DynamicDataPrefix) return dP.FieldAsInterface(strings.Split(dnVal, NestingSep)) } @@ -55,7 +56,8 @@ func DPDynamicInterface(dnVal string, dP DataProvider) (interface{}, error) { // DPDynamicString returns the string value of the field if the path is dynamic func DPDynamicString(dnVal string, dP DataProvider) (string, error) { - if strings.HasPrefix(dnVal, DynamicDataPrefix) { + if strings.HasPrefix(dnVal, DynamicDataPrefix) && + dnVal != DynamicDataPrefix { dnVal = strings.TrimPrefix(dnVal, DynamicDataPrefix) return dP.FieldAsString(strings.Split(dnVal, NestingSep)) } diff --git a/utils/reflect.go b/utils/reflect.go index 08e4d3394..1a164ef5e 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -30,6 +30,9 @@ import ( // StringToInterface will parse string into supported types // if no other conversion possible, original string will be returned func StringToInterface(s string) interface{} { + if s == EmptyString { + return s + } // int64 if i, err := strconv.ParseInt(s, 10, 64); err == nil { return i diff --git a/utils/reflect_test.go b/utils/reflect_test.go index 5b4024578..a4685d6c1 100644 --- a/utils/reflect_test.go +++ b/utils/reflect_test.go @@ -231,6 +231,9 @@ func TestStringToInterface(t *testing.T) { if res := StringToInterface("1"); res != int64(1) { t.Error("not parsing int") } + if res := StringToInterface(""); res != "" { + t.Error("not parsing string") + } if res := StringToInterface("true"); res != true { t.Error("not parsing bool") } diff --git a/utils/rsrfield.go b/utils/rsrfield.go index c34b815ce..ea2915025 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -85,7 +85,7 @@ func NewRSRField(fldStr string) (fld *RSRField, err error) { rsrField.filters = filters return rsrField, nil } - if !strings.HasPrefix(fldStr, REGEXP_PREFIX) { + if !strings.HasPrefix(fldStr, DynamicDataPrefix) { rsrField.Id = fldStr rsrField.filters = filters return rsrField, nil @@ -210,7 +210,7 @@ func NewRSRFilter(fltrVal string) (rsrFltr *RSRFilter, err error) { } } rsrFltr.filterRule = fltrVal - if fltrVal[:1] == REGEXP_PREFIX { + if fltrVal[:1] == DynamicDataPrefix { if rsrFltr.fltrRgxp, err = regexp.Compile(fltrVal[1:]); err != nil { return nil, err } @@ -242,7 +242,7 @@ func (rsrFltr *RSRFilter) Pass(val string) bool { if rsrFltr.filterRule == "" { return !rsrFltr.negative } - if rsrFltr.filterRule[:1] == REGEXP_PREFIX { + if rsrFltr.filterRule[:1] == DynamicDataPrefix { return rsrFltr.fltrRgxp.MatchString(val) != rsrFltr.negative } if rsrFltr.filterRule == "^$" { // Special case to test empty value