Adding initial xmlconfig structure with tests

This commit is contained in:
DanB
2014-03-22 16:49:06 +01:00
parent c6de153685
commit a93ecfc049
3 changed files with 282 additions and 0 deletions

71
cdre/libfixedwidth.go Normal file
View File

@@ -0,0 +1,71 @@
/*
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 cdre
import (
"fmt"
"strconv"
)
// Used as generic function logic for various fields
// Attributes
// source - the base source
// maxLen - the maximum field lenght
// stripAllowed - whether we allow stripping of chars in case of source bigger than the maximum allowed
// lStrip - if true, strip from beginning of the string
// lPadding - if true, add chars at the beginning of the string
// paddingChar - the character wich will be used to fill the existing
func filterField(source string, maxLen int, stripAllowed, lStrip, lPadding, padWithZero bool) (string, error) {
if len(source) == maxLen { // the source is exactly the maximum length
return source, nil
}
if len(source) > maxLen { //the source is bigger than allowed
if !stripAllowed {
return "", fmt.Errorf("source %s is bigger than the maximum allowed length %d", source, maxLen)
}
if !lStrip {
return source[:maxLen], nil
} else {
diffIndx := len(source) - maxLen
return source[diffIndx:], nil
}
} else { //the source is smaller as the maximum allowed
paddingString := "%"
if padWithZero {
paddingString += "0" // it will not work for rPadding but this is not needed
}
if !lPadding {
paddingString += "-"
}
paddingString += strconv.Itoa(maxLen) + "s"
return fmt.Sprintf(paddingString, source), nil
}
}
/*
type XmlCdreConfig struct {
XMLName xml.Name `xml:"configuration"`
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
Header XMLFWCdrHeader `xml:"header"`
Content XMLFWCdrContent `xml:"content"`
Footer XMLFWCdrFooter `xml:"footer"`
}
*/

103
config/xmlconfig.go Normal file
View File

@@ -0,0 +1,103 @@
/*
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 config
import (
"encoding/xml"
"errors"
"github.com/cgrates/cgrates/utils"
"io"
)
// Decodes a reader enforcing specific format of the configuration file
func ParseCgrXmlConfig(reader io.Reader) (*CgrXmlCfgDocument, error) {
xmlConfig := new(CgrXmlCfgDocument)
decoder := xml.NewDecoder(reader)
if err := decoder.Decode(xmlConfig); err != nil {
return nil, err
}
return xmlConfig, nil
}
// Define a format for configuration file, one doc contains more configuration instances, identified by section, type and id
type CgrXmlCfgDocument struct {
XMLName xml.Name `xml:"document"`
Type string `xml:"type,attr"`
Configurations []*CgrXmlConfiguration `xml:"configuration"`
}
// Storage for raw configuration
type CgrXmlConfiguration struct {
XMLName xml.Name `xml:"configuration"`
Section string `xml:"section,attr"`
Type string `xml:"type,attr"`
Id string `xml:"id,attr"`
RawConfig []byte `xml:",innerxml"`
}
// The CdrExporter Fixed Width configuration instance
type CgrXmlCdreFwCfg struct {
Header *CgrXmlCfgCdrHeader `xml:"header"`
Content *CgrXmlCfgCdrContent `xml:"content"`
Trailer *CgrXmlCfgCdrTrailer `xml:"trailer"`
}
// CDR header
type CgrXmlCfgCdrHeader struct {
XMLName xml.Name `xml:"header"`
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
}
// CDR content
type CgrXmlCfgCdrContent struct {
XMLName xml.Name `xml:"content"`
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
}
// CDR trailer
type CgrXmlCfgCdrTrailer struct {
XMLName xml.Name `xml:"trailer"`
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
}
// CDR field
type CgrXmlCfgCdrField struct {
XMLName xml.Name `xml:"field"`
Name string `xml:"name,attr"`
Type string `xml:"type,attr"`
Width string `xml:"width,attr"`
}
func (xmlCfg *CgrXmlCfgDocument) GetCdreFWCfg(instName string) (*CgrXmlCdreFwCfg, error) {
cdrefwCfg := new(CgrXmlCdreFwCfg)
for _, cfgInst := range xmlCfg.Configurations {
if cfgInst.Section != "cdre" || cfgInst.Type != utils.CDR_FIXED_WIDTH || cfgInst.Id != instName {
continue
}
rawConfig := append([]byte("<element>"), cfgInst.RawConfig...)
rawConfig = append(rawConfig, []byte("</element>")...)
if err := xml.Unmarshal(rawConfig, cdrefwCfg); err != nil { // Encapsulate the rawConfig in one element so we can Unmarshall
return nil, err
} else if cdrefwCfg == nil {
return nil, errors.New("Could not unmarshal CgrXmlCdreFwCfg")
}
return cdrefwCfg, nil
}
return nil, nil
}

108
config/xmlconfig_test.go Normal file
View File

@@ -0,0 +1,108 @@
/*
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 config
import (
"strings"
"testing"
)
var cfgDoc *CgrXmlCfgDocument // Will be populated by first test
func TestParseXmlConfig(t *testing.T) {
cfgXmlStr := `<?xml version="1.0" encoding="UTF-8" ?>
<document type="cgrates/xml">
<configuration section="cdre" type="cdr_fixed_width" id="CDRE-FW1">
<header>
<fields>
<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" id="exportid" lpadding="true" padding_char="0" width="5"/>
<field name="CutOffTime" type="metatag" id="time_lastcdr" layout="020106150400" width="12"/>
<field name="FileCreationfTime" type="metatag" id="time_now" layout="020106150400" width="12"/>
<field name="FileSpecificationVersion" type="constant" value="01" width="2"/>
<field name="Filler2" type="filler" width="105"/>
</fields>
</header>
<content>
<fields>
<field name="RecordType" type="constant" value="20" width="2"/>
<field name="SIPTrunkID" type="cdrfield" id="cgrid" width="12"/>
<field name="ConnectionNumber" type="cdrfield" id="subject" strip="left" padding="left" padding_char="0" width="5"/>
<field name="ANumber" type="cdrfield" id="cli" strip="xright" width="15"/>
<field name="CalledNumber" type="cdrfield" id="destination" strip="xright" width="24"/>
<field name="ServiceType" type="constant" value="02" width="2"/>
<field name="ServiceIdentification" type="constant" value="11" width="4"/>
<field name="StartChargingDateTime" type="cdrfield" id="start_time" layout="020106150400" width="12"/>
<field name="ChargeableTime" type="cdrfield" id="duration" width="6"/>
<field name="DataVolume" type="filler" width="6"/>
<field name="TaxCode" type="constant" value="1" width="1"/>
<field name="OperatorTAPCode" type="cdrfield" id="opertapcode" width="2"/>
<field name="ProductNumber" type="cdrfield" id="productnumber" width="5"/>
<field name="NetworkSubtype" type="constant" value="3" width="1"/>
<field name="SessionID" type="cdrfield" id="accid" width="16"/>
<field name="VolumeUP" type="filler" width="8"/>
<field name="VolumeDown" type="filler" width="8"/>
<field name="TerminatingOperator" type="concatenated_cdrfield" id="tapcode,operatorcode" width="5"/>
<field name="EndCharge" type="metatag" id="total_cost" lpadding="true" padding_char="0" width="9"/>
<field name="CallMaskingIndicator" type="cdrfield" id="calledmask" width="1"/>
</fields>
</content>
<trailer>
<fields>
<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" id="exportid" lpadding="true" padding_char="0" width="5"/>
<field name="TotalNrRecords" type="metatag" id="nr_cdrs" lpadding="true" padding_char="0" width="6"/>
<field name="TotalDurRecords" type="metatag" id="dur_cdrs" lpadding="true" padding_char="0" width="8"/>
<field name="EarliestCDRTime" type="metatag" id="first_cdr_time" layout="020106150400" width="12"/>
<field name="LatestCDRTime" type="metatag" id="last_cdr_time" layout="020106150400" width="12"/>
<field name="Filler1" type="filler" width="93"/>
</fields>
</trailer>
</configuration>
</document>`
var err error
reader := strings.NewReader(cfgXmlStr)
if cfgDoc, err = ParseCgrXmlConfig(reader); err != nil {
t.Error(err.Error())
} else if cfgDoc == nil {
t.Fatal("Could not parse xml configuration document")
}
}
func TestGetCdreFWCfg(t *testing.T) {
cdreFWCfg, err := cfgDoc.GetCdreFWCfg("CDRE-FW1")
if err != nil {
t.Error(err)
} else if cdreFWCfg == nil {
t.Error("Could not parse CdreFw instance")
}
if len(cdreFWCfg.Header.Fields) != 8 {
t.Error("Unexpected number of header fields parsed", len(cdreFWCfg.Header.Fields))
}
if len(cdreFWCfg.Content.Fields) != 20 {
t.Error("Unexpected number of content fields parsed", len(cdreFWCfg.Content.Fields))
}
if len(cdreFWCfg.Trailer.Fields) != 9 {
t.Error("Unexpected number of trailer fields parsed", len(cdreFWCfg.Trailer.Fields))
}
}