Basic implementation of fixed_width cdr export content

This commit is contained in:
DanB
2014-03-23 21:17:48 +01:00
parent 15f2a2cd82
commit 2bc4e3fb7e
15 changed files with 453 additions and 165 deletions

View File

@@ -21,7 +21,6 @@ package config
import (
"code.google.com/p/goconf/conf"
"errors"
"regexp"
"strings"
"github.com/cgrates/cgrates/utils"
@@ -46,22 +45,6 @@ func ConfigSlice(c *conf.ConfigFile, section, valName string) ([]string, error)
return cfgValStrs, nil
}
// Used to parse extra fields definition
func parseSearchReplaceFromFieldRule(fieldRule string) (string, *utils.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, &utils.ReSearchReplace{searchRegexp, allMatches[3]}, nil
}
func ParseRSRFields(configVal string) ([]*utils.RSRField, error) {
cfgValStrs := strings.Split(configVal, string(utils.CSV_SEP))
if len(cfgValStrs) == 1 && cfgValStrs[0] == "" { // Prevents returning iterable with empty value
@@ -73,14 +56,10 @@ func ParseRSRFields(configVal string) ([]*utils.RSRField, error) {
return nil, errors.New("Empty values in config slice")
}
if !strings.HasPrefix(cfgValStr, utils.REGEXP_SEP) {
rsrFields[idx] = &utils.RSRField{Id: cfgValStr}
continue // Nothing to be done for fields without ReSearchReplace rules
}
if fldId, reSrcRepl, err := parseSearchReplaceFromFieldRule(cfgValStr); err != nil {
if rsrField, err := utils.NewRSRField(cfgValStr); err != nil {
return nil, err
} else {
rsrFields[idx] = &utils.RSRField{fldId, reSrcRepl}
rsrFields[idx] = rsrField
}
}
return rsrFields, nil

View File

@@ -26,35 +26,6 @@ import (
"github.com/cgrates/cgrates/utils"
)
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, &utils.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, &utils.ReSearchReplace{regexp.MustCompile(`sip:\+49(\d+)\/@`), "0$1"}) {
t.Error("Unexpected ReSearchReplace parsed")
}
}
func TestParseRSRFields(t *testing.T) {
fields := `host,~sip_redirected_to:s/sip:\+49(\d+)@/0$1/,destination`
expectParsedFields := []*utils.RSRField{&utils.RSRField{Id: "host"},

View File

@@ -79,15 +79,14 @@ type CgrXmlCfgCdrTrailer struct {
// CDR field
type CgrXmlCfgCdrField struct {
XMLName xml.Name `xml:"field"`
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
Value string `xml:"value,attr"`
Width string `xml:"width,attr"`
Strip string `xml:"strip,attr"`
Padding string `xml:"padding,attr"`
PaddingChar string `xml:"padding_char,attr"`
Layout string `xml:"layout,attr"` // Eg. time format layout
XMLName xml.Name `xml:"field"`
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
Value string `xml:"value,attr"`
Width int `xml:"width,attr"` // Field width
Strip string `xml:"strip,attr"` // Strip strategy in case value is bigger than field width <""|left|xleft|right|xright>
Padding string `xml:"padding,attr"` // Padding strategy in case of value is smaller than width <""left|zeroleft|right>
Layout string `xml:"layout,attr"` // Eg. time format layout
}
// Avoid building from raw config string always, so build cache here

View File

@@ -34,7 +34,7 @@ func TestParseXmlConfig(t *testing.T) {
<field name="RecordType" type="constant" value="10" width="2"/>
<field name="Filler1" type="filler" width="3"/>
<field name="NetworkProviderCode" type="constant" value="VOI" width="3"/>
<field name="FileSeqNr" type="metatag" value="exportid" padding="left" padding_char="0" width="5"/>
<field name="FileSeqNr" type="metatag" value="exportid" padding="zeroleft" width="5"/>
<field name="CutOffTime" type="metatag" value="time_lastcdr" layout="020106150400" width="12"/>
<field name="FileCreationfTime" type="metatag" value="time_now" layout="020106150400" width="12"/>
<field name="FileSpecificationVersion" type="constant" value="01" width="2"/>
@@ -45,7 +45,7 @@ func TestParseXmlConfig(t *testing.T) {
<fields>
<field name="RecordType" type="constant" value="20" width="2"/>
<field name="SIPTrunkID" type="cdrfield" value="cgrid" width="12"/>
<field name="ConnectionNumber" type="cdrfield" value="subject" strip="left" padding="left" padding_char="0" width="5"/>
<field name="ConnectionNumber" type="cdrfield" value="subject" strip="left" padding="left" width="5"/>
<field name="ANumber" type="cdrfield" value="cli" strip="xright" width="15"/>
<field name="CalledNumber" type="cdrfield" value="destination" strip="xright" width="24"/>
<field name="ServiceType" type="constant" value="02" width="2"/>
@@ -61,7 +61,7 @@ func TestParseXmlConfig(t *testing.T) {
<field name="VolumeUP" type="filler" width="8"/>
<field name="VolumeDown" type="filler" width="8"/>
<field name="TerminatingOperator" type="concatenated_cdrfield" value="tapcode,operatorcode" width="5"/>
<field name="EndCharge" type="metatag" value="total_cost" padding="left" padding_char="0" width="9"/>
<field name="EndCharge" type="cdrfield" value="cost" padding="zeroleft" width="9"/>
<field name="CallMaskingIndicator" type="cdrfield" value="calledmask" width="1"/>
</fields>
</content>
@@ -70,9 +70,9 @@ func TestParseXmlConfig(t *testing.T) {
<field name="RecordType" type="constant" value="90" width="2"/>
<field name="Filler1" type="filler" width="3"/>
<field name="NetworkProviderCode" type="constant" value="VOI" width="3"/>
<field name="FileSeqNr" type="metatag" value="exportid" padding="left" padding_char="0" width="5"/>
<field name="TotalNrRecords" type="metatag" value="nr_cdrs" padding="left" padding_char="0" width="6"/>
<field name="TotalDurRecords" type="metatag" value="dur_cdrs" padding="left" padding_char="0" width="8"/>
<field name="FileSeqNr" type="metatag" value="exportid" padding="zeroleft" width="5"/>
<field name="TotalNrRecords" type="metatag" value="nr_cdrs" padding="zeroleft" width="6"/>
<field name="TotalDurRecords" type="metatag" value="dur_cdrs" padding="zeroleft" width="8"/>
<field name="EarliestCDRTime" type="metatag" value="first_cdr_time" layout="020106150400" width="12"/>
<field name="LatestCDRTime" type="metatag" value="last_cdr_time" layout="020106150400" width="12"/>
<field name="Filler1" type="filler" width="93"/>