diff --git a/cdre/cdrexporter.go b/cdre/cdrexporter.go index c21185d0a..1c227e9e8 100644 --- a/cdre/cdrexporter.go +++ b/cdre/cdrexporter.go @@ -37,6 +37,7 @@ const ( CONSTANT = "constant" METATAG = "metatag" CONCATENATED_CDRFIELD = "concatenated_cdrfield" + COMBIMED = "combimed" HTTP_POST = "http_post" META_EXPORTID = "export_id" META_TIMENOW = "time_now" diff --git a/config/cdreconfig.go b/config/cdreconfig.go index 2dee8fbea..7fea319d0 100644 --- a/config/cdreconfig.go +++ b/config/cdreconfig.go @@ -90,6 +90,7 @@ type CdreCdrField struct { Strip string Padding string Layout string + Filter *utils.RSRField Mandatory bool valueAsRsrField *utils.RSRField // Cached if the need arrises } diff --git a/config/xmlcdre.go b/config/xmlcdre.go index bd0be8cea..54315ee40 100644 --- a/config/xmlcdre.go +++ b/config/xmlcdre.go @@ -105,26 +105,28 @@ 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 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 - Mandatory bool `xml:"mandatory,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported - valueAsRsrField *utils.RSRField // Cached if the need arrises + 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 + Filter string `xml:"filter,attr"` // Eg. combimed filters + Mandatory bool `xml:"mandatory,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported + valueAsRsrField *utils.RSRField // Cached if the need arrises + filterAsRsrField *utils.RSRField } func (cdrFld *CgrXmlCfgCdrField) populateRSRField() (err error) { - if cdrFld.Type != utils.CDRFIELD { // We only need rsrField in case of cdrfield type - return nil - } - if cdrFld.valueAsRsrField, err = utils.NewRSRField(cdrFld.Value); err != nil { - return err - } - return nil + cdrFld.valueAsRsrField, err = utils.NewRSRField(cdrFld.Value) + return err +} + +func (cdrFld *CgrXmlCfgCdrField) populateFltrRSRField() (err error) { + cdrFld.filterAsRsrField, err = utils.NewRSRField(cdrFld.Filter) + return err } func (cdrFld *CgrXmlCfgCdrField) ValueAsRSRField() *utils.RSRField { @@ -140,6 +142,7 @@ func (cdrFld *CgrXmlCfgCdrField) AsCdreCdrField() *CdreCdrField { Strip: cdrFld.Strip, Padding: cdrFld.Padding, Layout: cdrFld.Layout, + Filter: cdrFld.filterAsRsrField, Mandatory: cdrFld.Mandatory, valueAsRsrField: cdrFld.valueAsRsrField, } diff --git a/config/xmlcdre_test.go b/config/xmlcdre_test.go index dae9b5700..c1802e0c3 100644 --- a/config/xmlcdre_test.go +++ b/config/xmlcdre_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package config import ( + "fmt" "github.com/cgrates/cgrates/utils" "reflect" "strings" @@ -38,12 +39,12 @@ func TestXmlCdreCfgPopulateCdreRSRFIeld(t *testing.T) { if recv := cdreField.ValueAsRSRField(); !reflect.DeepEqual(valRSRField, recv) { t.Errorf("Expecting %v, received %v", valRSRField, recv) } - cdreField = CgrXmlCfgCdrField{Name: "TEST1", Type: "constant", Value: `someval`} + /*cdreField = CgrXmlCfgCdrField{Name: "TEST1", Type: "constant", Value: `someval`} if err := cdreField.populateRSRField(); err != nil { t.Error("Unexpected error: ", err.Error()) } else if cdreField.valueAsRsrField != nil { t.Error("Should not load the RSRField") - } + }*/ } func TestXmlCdreCfgParseXmlConfig(t *testing.T) { @@ -119,6 +120,7 @@ func TestXmlCdreCfgParseXmlConfig(t *testing.T) { + @@ -155,7 +157,7 @@ func TestXmlCdreCfgGetCdreCfg(t *testing.T) { if cdreCsvCfg1 == nil { t.Error("Could not parse CdreFw instance") } - if len(cdreCsvCfg1["CHECK-CSV1"].Content.Fields) != 5 { + if len(cdreCsvCfg1["CHECK-CSV1"].Content.Fields) != 6 { t.Error("Unexpected number of content fields parsed", len(cdreCsvCfg1["CHECK-CSV1"].Content.Fields)) } } @@ -185,6 +187,7 @@ func TestXmlCdreCfgAsCdreConfig(t *testing.T) { + @@ -217,18 +220,21 @@ func TestXmlCdreCfgAsCdreConfig(t *testing.T) { MaskLength: 1, ExportDir: "/var/log/cgrates/cdre", } + fltrCombiMed, _ := utils.NewRSRField("~mediation_runid:s/DEFAULT/SECOND_RUN/") eCdreCfg.HeaderFields = []*CdreCdrField{ &CdreCdrField{ - Name: "TypeOfRecord", - Type: "constant", - Value: "10", - Width: 2}, + Name: "TypeOfRecord", + Type: "constant", + Value: "10", + Width: 2, + valueAsRsrField: &utils.RSRField{Id: "10"}}, &CdreCdrField{ - Name: "LastCdr", - Type: "metatag", - Value: "last_cdr_time", - Layout: "020106150400", - Width: 12}, + Name: "LastCdr", + Type: "metatag", + Value: "last_cdr_time", + Layout: "020106150400", + Width: 12, + valueAsRsrField: &utils.RSRField{Id: "last_cdr_time"}}, } eCdreCfg.ContentFields = []*CdreCdrField{ &CdreCdrField{ @@ -246,36 +252,53 @@ func TestXmlCdreCfgAsCdreConfig(t *testing.T) { valueAsRsrField: &utils.RSRField{Id: "productid"}, }, &CdreCdrField{ - Name: "NetworkId", - Type: "constant", - Value: "3", - Width: 1, + Name: "NetworkId", + Type: "constant", + Value: "3", + Width: 1, + valueAsRsrField: &utils.RSRField{Id: "3"}, }, &CdreCdrField{ - Name: "FromHttpPost1", - Type: "http_post", - Value: "https://localhost:8000", - Width: 10, - Strip: "xright", - Padding: "left", + Name: "FromHttpPost1", + Type: "http_post", + Value: "https://localhost:8000", + Width: 10, + Strip: "xright", + Padding: "left", + valueAsRsrField: &utils.RSRField{Id: "https://localhost:8000"}, + }, + &CdreCdrField{ + Name: "CombiMed1", + Type: "combimed", + Value: "cost", + Width: 10, + Strip: "xright", + Padding: "left", + Filter: fltrCombiMed, + valueAsRsrField: &utils.RSRField{Id: "cost"}, }, } eCdreCfg.TrailerFields = []*CdreCdrField{ &CdreCdrField{ - Name: "DistributorCode", - Type: "constant", - Value: "VOI", - Width: 3, + Name: "DistributorCode", + Type: "constant", + Value: "VOI", + Width: 3, + valueAsRsrField: &utils.RSRField{Id: "VOI"}, }, &CdreCdrField{ - Name: "FileSeqNr", - Type: "metatag", - Value: "export_id", - Width: 5, - Padding: "zeroleft", + Name: "FileSeqNr", + Type: "metatag", + Value: "export_id", + Width: 5, + Padding: "zeroleft", + valueAsRsrField: &utils.RSRField{Id: "export_id"}, }, } if rcvCdreCfg := xmlCdreCfgs["CDRE-FW2"].AsCdreConfig(); !reflect.DeepEqual(rcvCdreCfg, eCdreCfg) { + for _, fld := range rcvCdreCfg.ContentFields { + fmt.Printf("Fld: %+v\n", fld) + } t.Errorf("Expecting: %v, received: %v", eCdreCfg, rcvCdreCfg) } } diff --git a/config/xmlconfig.go b/config/xmlconfig.go index fbaedb7d7..b75b48b7a 100644 --- a/config/xmlconfig.go +++ b/config/xmlconfig.go @@ -109,12 +109,37 @@ func (xmlCfg *CgrXmlCfgDocument) cacheCdreCfgs() error { } else if cdreCfg == nil { return fmt.Errorf("Could not unmarshal CgrXmlCdreCfg: %s", cfgInst.Id) } + if cdreCfg.Header != nil { + // Cache rsr fields + for _, fld := range cdreCfg.Header.Fields { + if err := fld.populateRSRField(); err != nil { + return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) + } + if err := fld.populateFltrRSRField(); err != nil { + return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) + } + } + } if cdreCfg.Content != nil { // Cache rsr fields for _, fld := range cdreCfg.Content.Fields { if err := fld.populateRSRField(); err != nil { return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) } + if err := fld.populateFltrRSRField(); err != nil { + return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) + } + } + } + if cdreCfg.Trailer != nil { + // Cache rsr fields + for _, fld := range cdreCfg.Trailer.Fields { + if err := fld.populateRSRField(); err != nil { + return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) + } + if err := fld.populateFltrRSRField(); err != nil { + return fmt.Errorf("Populating field %s, error: %s", fld.Name, err.Error()) + } } } xmlCfg.cdres[cfgInst.Id] = cdreCfg