From 548a4ea64ffe5cae8071f6b6def0baa665547ef8 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 15 Mar 2014 16:30:17 +0100 Subject: [PATCH] Adding ReSearchReplace struct --- config/config.go | 53 ++++++++++++++--------------- utils/coreutils.go | 23 ------------- utils/researchreplace.go | 53 +++++++++++++++++++++++++++++ utils/researchreplace_test.go | 63 +++++++++++++++++++++++++++++++++++ utils/utils_test.go | 45 ------------------------- 5 files changed, 143 insertions(+), 94 deletions(-) create mode 100644 utils/researchreplace.go create mode 100644 utils/researchreplace_test.go diff --git a/config/config.go b/config/config.go index c7e302437..62b0d6ff3 100644 --- a/config/config.go +++ b/config/config.go @@ -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. - 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 - 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 . - 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. + 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 + CdrcRunDelay time.Duration // Sleep interval between consecutive runs, 0 to use automation via inotify + CdrcCdrType string // CDR file format . + 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 diff --git a/utils/coreutils.go b/utils/coreutils.go index f734c3895..a4c78c212 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -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) -} diff --git a/utils/researchreplace.go b/utils/researchreplace.go new file mode 100644 index 000000000..be0acbfaf --- /dev/null +++ b/utils/researchreplace.go @@ -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 +*/ + +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 +} diff --git a/utils/researchreplace_test.go b/utils/researchreplace_test.go new file mode 100644 index 000000000..bb98ff3ba --- /dev/null +++ b/utils/researchreplace_test.go @@ -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 +*/ + +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 := "" + 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") + } +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 0ac912dbd..1e5bcefb0 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -19,8 +19,6 @@ along with this program. If not, see 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 := "" - expectOut := "086517174963@127.0.0.1" - if outStr := RegexpSearchReplace(source, srchRgxp, replaceTpl); outStr != expectOut { - t.Error("Unexpected output from SearchReplace: ", outStr) - } -}