From a93ecfc0497bcf4f6df1a8268a1083f5fe042499 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 22 Mar 2014 16:49:06 +0100 Subject: [PATCH] Adding initial xmlconfig structure with tests --- cdre/libfixedwidth.go | 71 +++++++++++++++++++++++++ config/xmlconfig.go | 103 +++++++++++++++++++++++++++++++++++++ config/xmlconfig_test.go | 108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 cdre/libfixedwidth.go create mode 100644 config/xmlconfig.go create mode 100644 config/xmlconfig_test.go diff --git a/cdre/libfixedwidth.go b/cdre/libfixedwidth.go new file mode 100644 index 000000000..e698d38f7 --- /dev/null +++ b/cdre/libfixedwidth.go @@ -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 +*/ + +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"` +} +*/ diff --git a/config/xmlconfig.go b/config/xmlconfig.go new file mode 100644 index 000000000..752e5403e --- /dev/null +++ b/config/xmlconfig.go @@ -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 +*/ + +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(""), cfgInst.RawConfig...) + rawConfig = append(rawConfig, []byte("")...) + 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 +} diff --git a/config/xmlconfig_test.go b/config/xmlconfig_test.go new file mode 100644 index 000000000..4834e9330 --- /dev/null +++ b/config/xmlconfig_test.go @@ -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 +*/ + +package config + +import ( + "strings" + "testing" +) + +var cfgDoc *CgrXmlCfgDocument // Will be populated by first test + +func TestParseXmlConfig(t *testing.T) { + cfgXmlStr := ` + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
` + 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)) + } +}