Adding ReSearchReplace struct

This commit is contained in:
DanB
2014-03-15 16:30:17 +01:00
parent 3227434fd9
commit 548a4ea64f
5 changed files with 143 additions and 94 deletions

View File

@@ -84,32 +84,33 @@ type CGRConfig struct {
RaterBalancer string // balancer address host:port
BalancerEnabled bool
SchedulerEnabled bool
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []string // Extra fields to store in CDRs
CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
CdreCdrFormat string // Format of the exported CDRs. <csv>
CdreExtraFields []string // Extra fields list to add in exported CDRs
CdreDir string // Path towards exported cdrs directory
CdrcEnabled bool // Enable CDR client functionality
CdrcCdrs string // Address where to reach CDR server
CdrcCdrsMethod string // Mechanism to use when posting CDRs on server <http_cgr>
CdrcRunDelay time.Duration // Sleep interval between consecutive runs, if time unit missing, defaults to seconds, 0 to use automation via inotify
CdrcCdrType string // CDR file format <csv>.
CdrcCdrInDir string // Absolute path towards the directory where the CDRs are stored.
CdrcCdrOutDir string // Absolute path towards the directory where processed CDRs will be moved.
CdrcSourceId string // Tag identifying the source of the CDRs within CGRS database.
CdrcAccIdField string // Accounting id field identifier. Use index number in case of .csv cdrs.
CdrcReqTypeField string // Request type field identifier. Use index number in case of .csv cdrs.
CdrcDirectionField string // Direction field identifier. Use index numbers in case of .csv cdrs.
CdrcTenantField string // Tenant field identifier. Use index numbers in case of .csv cdrs.
CdrcTorField string // Type of Record field identifier. Use index numbers in case of .csv cdrs.
CdrcAccountField string // Account field identifier. Use index numbers in case of .csv cdrs.
CdrcSubjectField string // Subject field identifier. Use index numbers in case of .csv CDRs.
CdrcDestinationField string // Destination field identifier. Use index numbers in case of .csv cdrs.
CdrcSetupTimeField string // Setup time field identifier. Use index numbers in case of .csv cdrs.
CdrcAnswerTimeField string // Answer time field identifier. Use index numbers in case of .csv cdrs.
CdrcDurationField string // Duration field identifier. Use index numbers in case of .csv cdrs.
CdrcExtraFields []string // Field identifiers of the fields to add in extra fields section, special format in case of .csv "field1:index1,field2:index2"
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []string // Extra fields to store in CDRs
CDRSSearchReplaceRules map[string]*utils.ReSearchReplace // Key will be the field name, values their compiled ReSearchReplace rules
CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
CdreCdrFormat string // Format of the exported CDRs. <csv>
CdreExtraFields []string // Extra fields list to add in exported CDRs
CdreDir string // Path towards exported cdrs directory
CdrcEnabled bool // Enable CDR client functionality
CdrcCdrs string // Address where to reach CDR server
CdrcCdrsMethod string // Mechanism to use when posting CDRs on server <http_cgr>
CdrcRunDelay time.Duration // Sleep interval between consecutive runs, 0 to use automation via inotify
CdrcCdrType string // CDR file format <csv>.
CdrcCdrInDir string // Absolute path towards the directory where the CDRs are stored.
CdrcCdrOutDir string // Absolute path towards the directory where processed CDRs will be moved.
CdrcSourceId string // Tag identifying the source of the CDRs within CGRS database.
CdrcAccIdField string // Accounting id field identifier. Use index number in case of .csv cdrs.
CdrcReqTypeField string // Request type field identifier. Use index number in case of .csv cdrs.
CdrcDirectionField string // Direction field identifier. Use index numbers in case of .csv cdrs.
CdrcTenantField string // Tenant field identifier. Use index numbers in case of .csv cdrs.
CdrcTorField string // Type of Record field identifier. Use index numbers in case of .csv cdrs.
CdrcAccountField string // Account field identifier. Use index numbers in case of .csv cdrs.
CdrcSubjectField string // Subject field identifier. Use index numbers in case of .csv CDRs.
CdrcDestinationField string // Destination field identifier. Use index numbers in case of .csv cdrs.
CdrcSetupTimeField string // Setup time field identifier. Use index numbers in case of .csv cdrs.
CdrcAnswerTimeField string // Answer time field identifier. Use index numbers in case of .csv cdrs.
CdrcDurationField string // Duration field identifier. Use index numbers in case of .csv cdrs.
CdrcExtraFields []string // Extra fields to extract, special format in case of .csv "field1:index1,field2:index2"
SMEnabled bool
SMSwitchType string
SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer

View File

@@ -225,26 +225,3 @@ func ParseZeroRatingSubject(rateSubj string) (time.Duration, error) {
durStr := rateSubj[len(ZERO_RATING_SUBJECT_PREFIX):]
return time.ParseDuration(durStr)
}
// Used to parse extra fields definition search regexp rule and replace template
func ParseSearchReplaceStr(strRule string) (*regexp.Regexp, string, error) {
// String rule expected in the form ~hdr_name:s/match_rule/replace_rule/
getRuleRgxp := regexp.MustCompile(`~\w+:s\/(.+[^\\])\/(.+[^\\])\/`) // Make sure the separator / is not escaped in the rule
allMatches := getRuleRgxp.FindStringSubmatch(strRule)
if len(allMatches) != 3 { // Second and third groups are of interest to us
return nil, "", errors.New("Invalid Search&Replace rule.")
}
searchRegexp, err := regexp.Compile(allMatches[1])
if err != nil {
return nil, "", err
}
return searchRegexp, allMatches[2], nil
}
// Used to expand string sources, eg: extra fields
func RegexpSearchReplace(src string, searchRgxp *regexp.Regexp, replaceTpl string) string {
res := []byte{}
match := searchRgxp.FindStringSubmatchIndex(src)
res = searchRgxp.ExpandString(res, replaceTpl, src, match)
return string(res)
}

53
utils/researchreplace.go Normal file
View File

@@ -0,0 +1,53 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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 (
"errors"
"regexp"
)
// Regexp Search/Replace, used for example for field formatting
type ReSearchReplace struct {
SearchRegexp *regexp.Regexp
ReplaceTemplate string
}
func (self *ReSearchReplace) Process(source string) string {
res := []byte{}
match := self.SearchRegexp.FindStringSubmatchIndex(source)
res = self.SearchRegexp.ExpandString(res, self.ReplaceTemplate, source, match)
return string(res)
}
// Used to parse extra fields definition
func ParseSearchReplaceFromFieldRule(fieldRule string) (string, *ReSearchReplace, error) {
// String rule expected in the form ~hdr_name:s/match_rule/replace_rule/
getRuleRgxp := regexp.MustCompile(`~(\w+):s\/(.+[^\\])\/(.+[^\\])\/`) // Make sure the separator / is not escaped in the rule
allMatches := getRuleRgxp.FindStringSubmatch(fieldRule)
if len(allMatches) != 4 { // Second and third groups are of interest to us
return "", nil, errors.New("Invalid Search&Replace field rule.")
}
fieldName := allMatches[1]
searchRegexp, err := regexp.Compile(allMatches[2])
if err != nil {
return fieldName, nil, err
}
return fieldName, &ReSearchReplace{searchRegexp, allMatches[3]}, nil
}

View File

@@ -0,0 +1,63 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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 TestProcessReSearchReplace(t *testing.T) {
rsr := &ReSearchReplace{regexp.MustCompile(`sip:\+49(\d+)@(\d*\.\d*\.\d*\.\d*)`), "0$1@$2"}
source := "<sip:+4986517174963@127.0.0.1;transport=tcp>"
expectOut := "086517174963@127.0.0.1"
if outStr := rsr.Process(source); outStr != expectOut {
t.Error("Unexpected output from SearchReplace: ", outStr)
}
}
func TestParseSearchReplaceFromFieldRule(t *testing.T) {
// Normal case
fieldRule := `~sip_redirected_to:s/sip:\+49(\d+)@/0$1/`
field, regSrchRplc, err := ParseSearchReplaceFromFieldRule(fieldRule)
if len(field) == 0 || regSrchRplc == nil || err != nil {
t.Error("Failed parsing the field rule")
} else if !reflect.DeepEqual(regSrchRplc, &ReSearchReplace{regexp.MustCompile(`sip:\+49(\d+)@`), "0$1"}) {
t.Error("Unexpected ReSearchReplace parsed")
}
// Missing ~ prefix
fieldRule = `sip_redirected_to:s/sip:\+49(\d+)@/0$1/`
if _, _, err := ParseSearchReplaceFromFieldRule(fieldRule); err == nil {
t.Error("Parse error, field rule does not start with ~")
}
// Separator escaped
fieldRule = `~sip_redirected_to:s\/sip:\+49(\d+)@/0$1/`
if _, _, err := ParseSearchReplaceFromFieldRule(fieldRule); err == nil {
t.Error("Parse error, field rule does not contain correct number of separators")
}
// One extra separator but escaped
fieldRule = `~sip_redirected_to:s/sip:\+49(\d+)\/@/0$1/`
field, regSrchRplc, err = ParseSearchReplaceFromFieldRule(fieldRule)
if len(field) == 0 || regSrchRplc == nil || err != nil {
t.Error("Failed parsing the field rule")
} else if !reflect.DeepEqual(regSrchRplc, &ReSearchReplace{regexp.MustCompile(`sip:\+49(\d+)\/@`), "0$1"}) {
t.Error("Unexpected ReSearchReplace parsed")
}
}

View File

@@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"reflect"
"regexp"
"testing"
"time"
)
@@ -387,46 +385,3 @@ func TestParseZeroRatingSubject(t *testing.T) {
}
}
}
func TestParseSearchReplaceStr(t *testing.T) {
// Normal case
strRule := `~sip_redirected_to:s/sip:\+49(\d+)@/0$1/`
srchRgxp, replaceTpl, err := ParseSearchReplaceStr(strRule)
if srchRgxp == nil || len(replaceTpl) == 0 || err != nil {
t.Error("Could not parse the str rule")
} else if !reflect.DeepEqual(srchRgxp, regexp.MustCompile(`sip:\+49(\d+)@`)) {
t.Error("Unexpected regexp search parsed")
} else if replaceTpl != "0$1" {
t.Error("Unexpected replace template parsed")
}
// Missing ~ prefix
strRule = `sip_redirected_to:s/sip:\+49(\d+)@/0$1/`
if _, _, err := ParseSearchReplaceStr(strRule); err == nil {
t.Error("Parse error, srchrepl rule does not start with ~")
}
// Separator escaped
strRule = `~sip_redirected_to:s\/sip:\+49(\d+)@/0$1/`
if _, _, err := ParseSearchReplaceStr(strRule); err == nil {
t.Error("Parse error, srchrepl rule does not contain correct number of separators")
}
// One extra separator but escaped
strRule = `~sip_redirected_to:s/sip:\+49(\d+)\/@/0$1/`
srchRgxp, replaceTpl, err = ParseSearchReplaceStr(strRule)
if srchRgxp == nil || len(replaceTpl) == 0 || err != nil {
t.Error("Could not parse the str rule")
} else if !reflect.DeepEqual(srchRgxp, regexp.MustCompile(`sip:\+49(\d+)\/@`)) {
t.Error("Unexpected regexp search parsed")
} else if replaceTpl != "0$1" {
t.Error("Unexpected replace template parsed")
}
}
func TestRegexpSearchReplace(t *testing.T) {
srchRgxp := regexp.MustCompile(`sip:\+49(\d+)@(\d*\.\d*\.\d*\.\d*)`)
replaceTpl := "0$1@$2"
source := "<sip:+4986517174963@127.0.0.1;transport=tcp>"
expectOut := "086517174963@127.0.0.1"
if outStr := RegexpSearchReplace(source, srchRgxp, replaceTpl); outStr != expectOut {
t.Error("Unexpected output from SearchReplace: ", outStr)
}
}