mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-24 16:48:45 +05:00
CDRC cdr_type-> cdr_format, csv_separator->field_separator for better generics
This commit is contained in:
12
cdrc/cdrc.go
12
cdrc/cdrc.go
@@ -48,7 +48,7 @@ func NewCdrc(cdrcCfg *config.CdrcConfig, httpSkipTlsCheck bool, cdrServer *engin
|
||||
return nil, fmt.Errorf("Unsupported csv separator: %s", cdrcCfg.FieldSeparator)
|
||||
}
|
||||
csvSepRune, _ := utf8.DecodeRune([]byte(cdrcCfg.FieldSeparator))
|
||||
cdrc := &Cdrc{cdrsAddress: cdrcCfg.CdrsAddress, cdrType: cdrcCfg.CdrType, cdrInDir: cdrcCfg.CdrInDir, cdrOutDir: cdrcCfg.CdrOutDir,
|
||||
cdrc := &Cdrc{cdrsAddress: cdrcCfg.CdrsAddress, CdrFormat: cdrcCfg.CdrFormat, cdrInDir: cdrcCfg.CdrInDir, cdrOutDir: cdrcCfg.CdrOutDir,
|
||||
cdrSourceId: cdrcCfg.CdrSourceId, runDelay: cdrcCfg.RunDelay, csvSep: csvSepRune, cdrFields: cdrcCfg.CdrFields, httpSkipTlsCheck: httpSkipTlsCheck, cdrServer: cdrServer}
|
||||
// Before processing, make sure in and out folders exist
|
||||
for _, dir := range []string{cdrc.cdrInDir, cdrc.cdrOutDir} {
|
||||
@@ -62,7 +62,7 @@ func NewCdrc(cdrcCfg *config.CdrcConfig, httpSkipTlsCheck bool, cdrServer *engin
|
||||
|
||||
type Cdrc struct {
|
||||
cdrsAddress,
|
||||
cdrType,
|
||||
CdrFormat,
|
||||
cdrInDir,
|
||||
cdrOutDir,
|
||||
cdrSourceId string
|
||||
@@ -92,7 +92,7 @@ func (self *Cdrc) recordToStoredCdr(record []string) (*utils.StoredCdr, error) {
|
||||
var err error
|
||||
for _, cdrFldCfg := range self.cdrFields {
|
||||
var fieldVal string
|
||||
if utils.IsSliceMember([]string{CSV, FS_CSV}, self.cdrType) {
|
||||
if utils.IsSliceMember([]string{CSV, FS_CSV}, self.CdrFormat) {
|
||||
if cdrFldCfg.Type == utils.CDRFIELD {
|
||||
for _, cfgFieldRSR := range cdrFldCfg.Value {
|
||||
if cfgFieldRSR.IsStatic() {
|
||||
@@ -122,7 +122,7 @@ func (self *Cdrc) recordToStoredCdr(record []string) (*utils.StoredCdr, error) {
|
||||
return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type)
|
||||
}
|
||||
} else { // Modify here when we add more supported cdr formats
|
||||
return nil, fmt.Errorf("Unsupported CDR file format: %s", self.cdrType)
|
||||
return nil, fmt.Errorf("Unsupported CDR file format: %s", self.CdrFormat)
|
||||
}
|
||||
switch cdrFldCfg.CdrFieldId {
|
||||
case utils.TOR:
|
||||
@@ -169,7 +169,7 @@ func (self *Cdrc) processCdrDir() error {
|
||||
engine.Logger.Info(fmt.Sprintf("<Cdrc> Parsing folder %s for CDR files.", self.cdrInDir))
|
||||
filesInDir, _ := ioutil.ReadDir(self.cdrInDir)
|
||||
for _, file := range filesInDir {
|
||||
if self.cdrType != FS_CSV || path.Ext(file.Name()) != ".csv" {
|
||||
if self.CdrFormat != FS_CSV || path.Ext(file.Name()) != ".csv" {
|
||||
go func() { //Enable async processing here
|
||||
if err := self.processFile(path.Join(self.cdrInDir, file.Name())); err != nil {
|
||||
engine.Logger.Err(fmt.Sprintf("Processing file %s, error: %s", file, err.Error()))
|
||||
@@ -195,7 +195,7 @@ func (self *Cdrc) trackCDRFiles() (err error) {
|
||||
for {
|
||||
select {
|
||||
case ev := <-watcher.Event:
|
||||
if ev.IsCreate() && (self.cdrType != FS_CSV || path.Ext(ev.Name) != ".csv") {
|
||||
if ev.IsCreate() && (self.CdrFormat != FS_CSV || path.Ext(ev.Name) != ".csv") {
|
||||
go func() { //Enable async processing here
|
||||
if err = self.processFile(ev.Name); err != nil {
|
||||
engine.Logger.Err(fmt.Sprintf("Processing file %s, error: %s", ev.Name, err.Error()))
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestRecordForkCdr(t *testing.T) {
|
||||
cgrConfig, _ := config.NewDefaultCGRConfig()
|
||||
cdrcConfig := cgrConfig.CdrcInstances[0]
|
||||
cdrcConfig.CdrFields = append(cdrcConfig.CdrFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.CDRFIELD, CdrFieldId: "supplier", Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}})
|
||||
cdrc := &Cdrc{cdrType: CSV, cdrSourceId: "TEST_CDRC", cdrFields: cdrcConfig.CdrFields}
|
||||
cdrc := &Cdrc{CdrFormat: CSV, cdrSourceId: "TEST_CDRC", cdrFields: cdrcConfig.CdrFields}
|
||||
cdrRow := []string{"firstField", "secondField"}
|
||||
_, err := cdrc.recordToStoredCdr(cdrRow)
|
||||
if err == nil {
|
||||
@@ -152,7 +152,7 @@ func TestDnTdmCdrs(t *testing.T) {
|
||||
utils.ANSWER_TIME: &utils.RSRField{Id: "4"},
|
||||
utils.USAGE: usageFld,
|
||||
}
|
||||
cdrc := &Cdrc{cgrConfig.CdrcCdrs, cgrConfig.CdrcCdrType, cgrConfig.CdrcCdrInDir, cgrConfig.CdrcCdrOutDir, cgrConfig.CdrcSourceId, cgrConfig.CdrcRunDelay, ',',
|
||||
cdrc := &Cdrc{cgrConfig.CdrcCdrs, cgrConfig.CdrcCdrFormat, cgrConfig.CdrcCdrInDir, cgrConfig.CdrcCdrOutDir, cgrConfig.CdrcSourceId, cgrConfig.CdrcRunDelay, ',',
|
||||
cgrConfig.CdrcCdrFields, new(cdrs.CDRS), nil}
|
||||
cdrsContent := bytes.NewReader([]byte(tdmCdrs))
|
||||
csvReader := csv.NewReader(cdrsContent)
|
||||
|
||||
@@ -35,8 +35,8 @@ func NewCdrcConfigFromCgrXmlCdrcCfg(id string, xmlCdrcCfg *CgrXmlCdrcCfg) (*Cdrc
|
||||
if xmlCdrcCfg.CdrsAddress != nil {
|
||||
cdrcCfg.CdrsAddress = *xmlCdrcCfg.CdrsAddress
|
||||
}
|
||||
if xmlCdrcCfg.CdrType != nil {
|
||||
cdrcCfg.CdrType = *xmlCdrcCfg.CdrType
|
||||
if xmlCdrcCfg.CdrFormat != nil {
|
||||
cdrcCfg.CdrFormat = *xmlCdrcCfg.CdrFormat
|
||||
}
|
||||
if xmlCdrcCfg.FieldSeparator != nil {
|
||||
cdrcCfg.FieldSeparator = *xmlCdrcCfg.FieldSeparator
|
||||
@@ -47,6 +47,9 @@ func NewCdrcConfigFromCgrXmlCdrcCfg(id string, xmlCdrcCfg *CgrXmlCdrcCfg) (*Cdrc
|
||||
if xmlCdrcCfg.CdrInDir != nil {
|
||||
cdrcCfg.CdrInDir = *xmlCdrcCfg.CdrInDir
|
||||
}
|
||||
if xmlCdrcCfg.CdrOutDir != nil {
|
||||
cdrcCfg.CdrOutDir = *xmlCdrcCfg.CdrOutDir
|
||||
}
|
||||
if xmlCdrcCfg.CdrSourceId != nil {
|
||||
cdrcCfg.CdrSourceId = *xmlCdrcCfg.CdrSourceId
|
||||
}
|
||||
@@ -54,7 +57,7 @@ func NewCdrcConfigFromCgrXmlCdrcCfg(id string, xmlCdrcCfg *CgrXmlCdrcCfg) (*Cdrc
|
||||
cdrcCfg.CdrFields = nil // Reinit the fields, so we do not inherit from defaults here
|
||||
}
|
||||
for _, xmlCdrField := range xmlCdrcCfg.CdrFields {
|
||||
if cdrFld, err := NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlCdrField, cdrcCfg.CdrType == utils.CDRE_FIXED_WIDTH); err != nil {
|
||||
if cdrFld, err := NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlCdrField, cdrcCfg.CdrFormat == utils.CDRE_FIXED_WIDTH); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, cdrFld)
|
||||
@@ -82,7 +85,7 @@ func NewDefaultCdrcConfig() *CdrcConfig {
|
||||
Id: utils.META_DEFAULT,
|
||||
Enabled: false,
|
||||
CdrsAddress: "",
|
||||
CdrType: utils.CSV,
|
||||
CdrFormat: utils.CSV,
|
||||
FieldSeparator: utils.FIELDS_SEP,
|
||||
RunDelay: time.Duration(0),
|
||||
CdrInDir: "/var/log/cgrates/cdrc/in",
|
||||
@@ -102,11 +105,11 @@ func NewCdrcConfigFromFileParams(c *conf.ConfigFile) (*CdrcConfig, error) {
|
||||
if hasOpt := c.HasOption("cdrc", "cdrs"); hasOpt {
|
||||
cdrcCfg.CdrsAddress, _ = c.GetString("cdrc", "cdrs")
|
||||
}
|
||||
if hasOpt := c.HasOption("cdrc", "cdr_type"); hasOpt {
|
||||
cdrcCfg.CdrType, _ = c.GetString("cdrc", "cdr_type")
|
||||
if hasOpt := c.HasOption("cdrc", "cdr_format"); hasOpt {
|
||||
cdrcCfg.CdrFormat, _ = c.GetString("cdrc", "cdr_format")
|
||||
}
|
||||
if hasOpt := c.HasOption("cdrc", "csv_separator"); hasOpt {
|
||||
cdrcCfg.FieldSeparator, _ = c.GetString("cdrc", "csv_separator")
|
||||
if hasOpt := c.HasOption("cdrc", "field_separator"); hasOpt {
|
||||
cdrcCfg.FieldSeparator, _ = c.GetString("cdrc", "field_separator")
|
||||
}
|
||||
if hasOpt := c.HasOption("cdrc", "run_delay"); hasOpt {
|
||||
durStr, _ := c.GetString("cdrc", "run_delay")
|
||||
@@ -187,7 +190,7 @@ type CdrcConfig struct {
|
||||
Id string // Configuration label
|
||||
Enabled bool // Enable/Disable the profile
|
||||
CdrsAddress string // The address where CDRs can be reached
|
||||
CdrType string // The type of CDR file to process <csv>
|
||||
CdrFormat string // The type of CDR file to process <csv>
|
||||
FieldSeparator string // The separator to use when reading csvs
|
||||
RunDelay time.Duration // Delay between runs, 0 for inotify driven requests
|
||||
CdrInDir string // Folder to process CDRs from
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestNewDefaultCdrcConfig(t *testing.T) {
|
||||
Id: utils.META_DEFAULT,
|
||||
Enabled: false,
|
||||
CdrsAddress: "",
|
||||
CdrType: utils.CSV,
|
||||
CdrFormat: utils.CSV,
|
||||
FieldSeparator: utils.FIELDS_SEP,
|
||||
RunDelay: time.Duration(0),
|
||||
CdrInDir: "/var/log/cgrates/cdrc/in",
|
||||
|
||||
@@ -230,7 +230,7 @@ func (self *CGRConfig) checkConfigSanity() error {
|
||||
if len(cdrcInst.CdrFields) == 0 {
|
||||
return errors.New("CdrC enabled but no fields to be processed defined!")
|
||||
}
|
||||
if cdrcInst.CdrType == utils.CSV {
|
||||
if cdrcInst.CdrFormat == utils.CSV {
|
||||
for _, cdrFld := range cdrcInst.CdrFields {
|
||||
for _, rsrFld := range cdrFld.Value {
|
||||
if _, errConv := strconv.Atoi(rsrFld.Id); errConv != nil {
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestConfigFromFile(t *testing.T) {
|
||||
cdrcCfg.Enabled = true
|
||||
cdrcCfg.CdrsAddress = "test"
|
||||
cdrcCfg.RunDelay = time.Duration(99) * time.Second
|
||||
cdrcCfg.CdrType = "test"
|
||||
cdrcCfg.CdrFormat = "test"
|
||||
cdrcCfg.FieldSeparator = ";"
|
||||
cdrcCfg.CdrInDir = "test"
|
||||
cdrcCfg.CdrOutDir = "test"
|
||||
|
||||
@@ -65,8 +65,8 @@ export_template = test # List of fields in the exported CDRs
|
||||
enabled = true # Enable CDR client functionality
|
||||
cdrs = test # Address where to reach CDR server
|
||||
run_delay = 99 # Period to sleep between two runs, 0 to use automation via inotify
|
||||
cdr_type = test # CDR file format <csv>.
|
||||
csv_separator =; # Csv separator, one character only and should be next to equal sign
|
||||
cdr_format = test # CDR file format <csv>.
|
||||
field_separator =; # Csv separator, one character only and should be next to equal sign
|
||||
cdr_in_dir = test # Absolute path towards the direccategoryy where the CDRs are kept (file scategoryed CDRs).
|
||||
cdr_out_dir = test # Absolute path towards the direccategoryy where processed CDRs will be moved after processing.
|
||||
cdr_source_id = test # Tag identifying the source of the CDRs within CGRS database.
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestSetDefaults(t *testing.T) {
|
||||
dfCfg, _ := NewDefaultCGRConfig()
|
||||
xmlCdrc.setDefaults()
|
||||
if xmlCdrc.CdrsAddress != dfCfg.CdrcCdrs ||
|
||||
xmlCdrc.CdrType != dfCfg.CdrcCdrType ||
|
||||
xmlCdrc.CdrFormat != dfCfg.CdrcCdrType ||
|
||||
xmlCdrc.CsvSeparator != dfCfg.CdrcCsvSep ||
|
||||
xmlCdrc.CdrInDir != dfCfg.CdrcCdrInDir ||
|
||||
xmlCdrc.CdrOutDir != dfCfg.CdrcCdrOutDir ||
|
||||
@@ -67,7 +67,7 @@ func TestParseXmlCdrcConfig(t *testing.T) {
|
||||
<configuration section="cdrc" id="CDRC-CSV1">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_type>csv</cdr_type>
|
||||
<cdr_format>csv</cdr_format>
|
||||
<field_separator>,</field_separator>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/var/log/cgrates/cdrc/in</cdr_in_dir>
|
||||
@@ -109,13 +109,13 @@ func TestGetCdrcCfgs(t *testing.T) {
|
||||
}
|
||||
enabled := true
|
||||
cdrsAddr := "internal"
|
||||
cdrType := "csv"
|
||||
cdrFormat := "csv"
|
||||
fldSep := ","
|
||||
runDelay := int64(0)
|
||||
cdrInDir := "/var/log/cgrates/cdrc/in"
|
||||
cdrOutDir := "/var/log/cgrates/cdrc/out"
|
||||
cdrSrcId := "freeswitch_csv"
|
||||
expectCdrc := &CgrXmlCdrcCfg{Enabled: &enabled, CdrsAddress: &cdrsAddr, CdrType: &cdrType, FieldSeparator: &fldSep,
|
||||
expectCdrc := &CgrXmlCdrcCfg{Enabled: &enabled, CdrsAddress: &cdrsAddr, CdrFormat: &cdrFormat, FieldSeparator: &fldSep,
|
||||
RunDelay: &runDelay, CdrInDir: &cdrInDir, CdrOutDir: &cdrOutDir, CdrSourceId: &cdrSrcId}
|
||||
accIdTag, reqTypeTag, dirTag, tntTag, categTag, acntTag, subjTag, dstTag, sTimeTag, aTimeTag, usageTag, extr1, extr2 := utils.ACCID,
|
||||
utils.REQTYPE, utils.DIRECTION, utils.TENANT, utils.CATEGORY, utils.ACCOUNT, utils.SUBJECT, utils.DESTINATION, utils.SETUP_TIME, utils.ANSWER_TIME, utils.USAGE, "extr1", "extr2"
|
||||
|
||||
@@ -57,7 +57,7 @@ type XmlCfgCdrField struct {
|
||||
type CgrXmlCdrcCfg struct {
|
||||
Enabled *bool `xml:"enabled"` // Enable/Disable the
|
||||
CdrsAddress *string `xml:"cdrs_address"` // The address where CDRs can be reached
|
||||
CdrType *string `xml:"cdr_type"` // The type of CDR to process <csv>
|
||||
CdrFormat *string `xml:"cdr_format"` // The type of CDR to process <csv>
|
||||
FieldSeparator *string `xml:"field_separator"` // The separator to use when reading csvs
|
||||
RunDelay *int64 `xml:"run_delay"` // Delay between runs
|
||||
CdrInDir *string `xml:"cdr_in_dir"` // Folder to process CDRs from
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
# enabled = false # Enable CDR client functionality
|
||||
# cdrs = internal # Address where to reach CDR server. <internal|127.0.0.1:2080>
|
||||
# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
|
||||
# cdr_type = csv # CDR file format <csv|freeswitch_csv>.
|
||||
# csv_separator = , # Separator used in case of csv files. One character only supported and needs to be right after equal sign
|
||||
# cdr_format = csv # CDR file format <csv|freeswitch_csv>.
|
||||
# field_separator = , # Separator used in case of csv files. One character only supported and needs to be right after equal sign
|
||||
# cdr_in_dir = /var/log/cgrates/cdrc/in # Absolute path towards the directory where the CDRs are stored.
|
||||
# cdr_out_dir = /var/log/cgrates/cdrc/out # Absolute path towards the directory where processed CDRs will be moved.
|
||||
# cdr_source_id = csv # Free form field, tag identifying the source of the CDRs within CGRS database.
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdrc" type="csv" id="CDRC-CSV2">
|
||||
<configuration section="cdrc" id="CDRC-CSV2">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_type>csv</cdr_type>
|
||||
<csv_separator>,</csv_separator>
|
||||
<field_separator>,</field_separator>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/tmp/cgrates/cdrc2/in</cdr_in_dir>
|
||||
<cdr_out_dir>/tmp/cgrates/cdrc2/out</cdr_out_dir>
|
||||
<cdr_source_id>csv2</cdr_source_id>
|
||||
<fields>
|
||||
<field id="tor" filter="~7:s/^(voice|data|sms)$/*$1/" />
|
||||
<field id="accid" filter="0" />
|
||||
<field id="reqtype" filter="^rated" />
|
||||
<field id="direction" filter="^*out" />
|
||||
<field id="tenant" filter="^cgrates.org" />
|
||||
<field id="category" filter="~7:s/^voice$/call/" />
|
||||
<field id="account" filter="3" />
|
||||
<field id="subject" filter="3" />
|
||||
<field id="destination" filter="~5:s/^0([1-9]\d+)$/+49$1/" />
|
||||
<field id="setup_time" filter="1" />
|
||||
<field id="answer_time" filter="1" />
|
||||
<field id="usage" filter="~9:s/^(\d+)$/${1}s/" />
|
||||
<field cdr_field="tor" filter="~7:s/^(voice|data|sms)$/*$1/" />
|
||||
<field cdr_field="accid" filter="0" />
|
||||
<field cdr_field="reqtype" filter="^rated" />
|
||||
<field cdr_field="direction" filter="^*out" />
|
||||
<field cdr_field="tenant" filter="^cgrates.org" />
|
||||
<field cdr_field="category" filter="~7:s/^voice$/call/" />
|
||||
<field cdr_field="account" filter="3" />
|
||||
<field cdr_field="subject" filter="3" />
|
||||
<field cdr_field="destination" filter="~5:s/^0([1-9]\d+)$/+49$1/" />
|
||||
<field cdr_field="setup_time" filter="1" />
|
||||
<field cdr_field="answer_time" filter="1" />
|
||||
<field cdr_field="usage" filter="~9:s/^(\d+)$/${1}s/" />
|
||||
</fields>
|
||||
</configuration>
|
||||
<configuration section="cdrc" type="csv" id="CDRC-CSV3">
|
||||
<configuration section="cdrc" id="CDRC-CSV3">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_type>csv</cdr_type>
|
||||
<csv_separator>;</csv_separator>
|
||||
<field_separator>;</field_separator>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/tmp/cgrates/cdrc3/in</cdr_in_dir>
|
||||
<cdr_out_dir>/tmp/cgrates/cdrc3/out</cdr_out_dir>
|
||||
<cdr_source_id>csv3</cdr_source_id>
|
||||
<fields>
|
||||
<field id="tor" filter="^*voice" />
|
||||
<field id="accid" filter="~3:s/^(\d{2})\.(\d{2})\.(\d{4})\s{2}(\d{2}):(\d{2}):(\d{2})$/$1$2$3$4$5$6/" />
|
||||
<field id="reqtype" filter="^rated" />
|
||||
<field id="direction" filter="^*out" />
|
||||
<field id="tenant" filter="^cgrates.org" />
|
||||
<field id="category" filter="^call" />
|
||||
<field id="account" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field id="subject" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field id="destination" filter="~1:s/^([1-9]\d+)$/+$1/" />
|
||||
<field id="setup_time" filter="4" />
|
||||
<field id="answer_time" filter="4" />
|
||||
<field id="usage" filter="~6:s/^(\d+)$/${1}s/" />
|
||||
<field cdr_field="tor" filter="^*voice" />
|
||||
<field cdr_field="accid" filter="~3:s/^(\d{2})\.(\d{2})\.(\d{4})\s{2}(\d{2}):(\d{2}):(\d{2})$/$1$2$3$4$5$6/" />
|
||||
<field cdr_field="reqtype" filter="^rated" />
|
||||
<field cdr_field="direction" filter="^*out" />
|
||||
<field cdr_field="tenant" filter="^cgrates.org" />
|
||||
<field cdr_field="category" filter="^call" />
|
||||
<field cdr_field="account" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="subject" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="destination" filter="~1:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="setup_time" filter="4" />
|
||||
<field cdr_field="answer_time" filter="4" />
|
||||
<field cdr_field="usage" filter="~6:s/^(\d+)$/${1}s/" />
|
||||
</fields>
|
||||
</configuration>
|
||||
<configuration section="cdre" type="fwv" id="CDRE-FW1">
|
||||
@@ -59,51 +59,51 @@
|
||||
<export_template>
|
||||
<header>
|
||||
<fields>
|
||||
<field name="ToR" type="constant" value="10" width="2" />
|
||||
<field name="Filler1" type="filler" width="3" />
|
||||
<field name="FileType" type="constant" value="SIP" width="3" />
|
||||
<field name="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field name="LastCdr" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field name="FileCreationfTime" type="metatag" value="time_now" layout="020106150405" width="12" />
|
||||
<field name="FileVersion" type="constant" value="01" width="2" />
|
||||
<field name="Filler2" type="filler" width="105" />
|
||||
<field tag="ToR" type="constant" value="10" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="FileType" type="constant" value="SIP" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="LastCdr" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="FileCreationfTime" type="metatag" value="time_now" layout="020106150405" width="12" />
|
||||
<field tag="FileVersion" type="constant" value="01" width="2" />
|
||||
<field tag="Filler2" type="filler" width="105" />
|
||||
</fields>
|
||||
</header>
|
||||
<content>
|
||||
<fields>
|
||||
<field name="ToR" type="constant" value="20" width="2" />
|
||||
<field name="Subject" type="cdrfield" value="subject" width="12" padding="right" mandatory="true" />
|
||||
<field name="ConnectionNumber" type="constant" value="00000" width="5" />
|
||||
<field name="CallerId" type="cdrfield" value="~callerid:s/\+(\d+)/00$1/" strip="xright" width="15" padding="right" />
|
||||
<field name="Destination" type="cdrfield" value="~destination:s/^\+311400(\d+)/$1/:s/^\+311412\d\d112/112/:s/^\+31(\d+)/0$1/:s/^\+(\d+)/00$1/" strip="xright" width="24" padding="right" mandatory="true" />
|
||||
<field name="TypeOfService" type="constant" value="00" width="2" />
|
||||
<field name="ServiceId" type="constant" value="11" width="4" padding="right" />
|
||||
<field name="AnswerTime" type="cdrfield" value="answer_time" layout="020106150405" width="12" mandatory="true" />
|
||||
<field name="Usage" type="cdrfield" value="usage" layout="seconds" width="6" padding="right" mandatory="true" />
|
||||
<field name="DataCounter" type="filler" width="6" />
|
||||
<field name="VatCode" type="constant" value="1" width="1" />
|
||||
<field name="NetworkId" type="constant" value="S1" width="2" />
|
||||
<field name="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field name="NetworkSubtype" type="constant" value="3" width="1" padding="left" />
|
||||
<field name="CgrId" type="cdrfield" value="cgrid" strip="xleft" width="16" paddingi="right" mandatory="true" />
|
||||
<field name="FillerVolume1" type="filler" width="8" />
|
||||
<field name="FillerVolume2" type="filler" width="8" />
|
||||
<field name="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field name="Cost" type="cdrfield" value="cost" padding="zeroleft" width="9" />
|
||||
<field name="MaskDestination" type="metatag" value="mask_destination" width="1" />
|
||||
<field tag="ToR" type="constant" value="20" width="2" />
|
||||
<field tag="Subject" type="cdrfield" value="subject" width="12" padding="right" mandatory="true" />
|
||||
<field tag="ConnectionNumber" type="constant" value="00000" width="5" />
|
||||
<field tag="CallerId" type="cdrfield" value="~callerid:s/\+(\d+)/00$1/" strip="xright" width="15" padding="right" />
|
||||
<field tag="Destination" type="cdrfield" value="~destination:s/^\+311400(\d+)/$1/:s/^\+311412\d\d112/112/:s/^\+31(\d+)/0$1/:s/^\+(\d+)/00$1/" strip="xright" width="24" padding="right" mandatory="true" />
|
||||
<field tag="TypeOfService" type="constant" value="00" width="2" />
|
||||
<field tag="ServiceId" type="constant" value="11" width="4" padding="right" />
|
||||
<field tag="AnswerTime" type="cdrfield" value="answer_time" layout="020106150405" width="12" mandatory="true" />
|
||||
<field tag="Usage" type="cdrfield" value="usage" layout="seconds" width="6" padding="right" mandatory="true" />
|
||||
<field tag="DataCounter" type="filler" width="6" />
|
||||
<field tag="VatCode" type="constant" value="1" width="1" />
|
||||
<field tag="NetworkId" type="constant" value="S1" width="2" />
|
||||
<field tag="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field tag="NetworkSubtype" type="constant" value="3" width="1" padding="left" />
|
||||
<field tag="CgrId" type="cdrfield" value="cgrid" strip="xleft" width="16" paddingi="right" mandatory="true" />
|
||||
<field tag="FillerVolume1" type="filler" width="8" />
|
||||
<field tag="FillerVolume2" type="filler" width="8" />
|
||||
<field tag="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field tag="Cost" type="cdrfield" value="cost" padding="zeroleft" width="9" />
|
||||
<field tag="MaskDestination" type="metatag" value="mask_destination" width="1" />
|
||||
</fields>
|
||||
</content>
|
||||
<trailer>
|
||||
<fields>
|
||||
<field name="ToR" type="constant" value="90" width="2" />
|
||||
<field name="Filler1" type="filler" width="3" />
|
||||
<field name="FileType" type="constant" value="SIP" width="3" />
|
||||
<field name="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field name="TotalRecords" type="metatag" value="cdrs_number" padding="zeroleft" width="6" />
|
||||
<field name="TotalDuration" type="metatag" value="cdrs_duration" padding="zeroleft" width="8" />
|
||||
<field name="FirstCdrTime" type="metatag" value="first_cdr_atime" layout="020106150405" width="12" />
|
||||
<field name="LastCdrTime" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field name="Filler1" type="filler" width="93" />
|
||||
<field tag="ToR" type="constant" value="90" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="FileType" type="constant" value="SIP" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="TotalRecords" type="metatag" value="cdrs_number" padding="zeroleft" width="6" />
|
||||
<field tag="TotalDuration" type="metatag" value="cdrs_duration" padding="zeroleft" width="8" />
|
||||
<field tag="FirstCdrTime" type="metatag" value="first_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="LastCdrTime" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="Filler1" type="filler" width="93" />
|
||||
</fields>
|
||||
</trailer>
|
||||
</export_template>
|
||||
|
||||
@@ -261,10 +261,11 @@ func (at *ActionTiming) Execute() (err error) {
|
||||
Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", ubId))
|
||||
return 0, err
|
||||
} else if ub.Disabled && a.ActionType != ENABLE_ACCOUNT {
|
||||
return 0, fmt.Errorf("User %s is disabled", ubId)
|
||||
return 0, fmt.Errorf("Account %s is disabled", ubId)
|
||||
}
|
||||
//Logger.Info(fmt.Sprintf("Executing %v on %v", a.ActionType, ub.Id))
|
||||
//Logger.Info(fmt.Sprintf("Executing %v on %+v", a.ActionType, ub))
|
||||
err = actionFunction(ub, nil, a)
|
||||
//Logger.Info(fmt.Sprintf("After execute, account: %+v", ub))
|
||||
accountingStorage.SetAccount(ub)
|
||||
return 0, nil
|
||||
})
|
||||
|
||||
@@ -379,15 +379,15 @@ func (rs *RedisStorage) RemoveRpAliases(tenantRtSubjects []*TenantRatingSubject)
|
||||
return err
|
||||
}
|
||||
for _, key := range alsKeys {
|
||||
alias, err := rs.GetRpAlias(key[len(RP_ALIAS_PREFIX):], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tntRSubj := range tenantRtSubjects {
|
||||
tenantPrfx := RP_ALIAS_PREFIX + tntRSubj.Tenant + utils.CONCATENATED_KEY_SEP
|
||||
if len(key) < len(tenantPrfx) || tenantPrfx != key[:len(tenantPrfx)] { // filter out the tenant for accounts
|
||||
continue
|
||||
}
|
||||
alias, err := rs.GetRpAlias(key[len(RP_ALIAS_PREFIX):], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tntRSubj.Subject != alias {
|
||||
continue
|
||||
}
|
||||
@@ -477,15 +477,15 @@ func (rs *RedisStorage) RemoveAccAliases(tenantAccounts []*TenantAccount) (err e
|
||||
return err
|
||||
}
|
||||
for _, key := range alsKeys {
|
||||
alias, err := rs.GetAccAlias(key[len(ACC_ALIAS_PREFIX):], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tntAcnt := range tenantAccounts {
|
||||
tenantPrfx := ACC_ALIAS_PREFIX + tntAcnt.Tenant + utils.CONCATENATED_KEY_SEP
|
||||
if len(key) < len(tenantPrfx) || tenantPrfx != key[:len(tenantPrfx)] { // filter out the tenant for accounts
|
||||
continue
|
||||
}
|
||||
alias, err := rs.GetAccAlias(key[len(ACC_ALIAS_PREFIX):], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tntAcnt.Account != alias {
|
||||
continue
|
||||
}
|
||||
|
||||
108
general_tests/acntacts_test.go
Normal file
108
general_tests/acntacts_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2014 ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can Storagetribute 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 WITH*out 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 general_tests
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var ratingDbAcntActs engine.RatingStorage
|
||||
var acntDbAcntActs engine.AccountingStorage
|
||||
|
||||
func TestAcntActsSetStorage(t *testing.T) {
|
||||
ratingDbAcntActs, _ = engine.NewMapStorageJson()
|
||||
engine.SetRatingStorage(ratingDbAcntActs)
|
||||
acntDbAcntActs, _ = engine.NewMapStorageJson()
|
||||
engine.SetAccountingStorage(acntDbAcntActs)
|
||||
}
|
||||
|
||||
func TestAcntActsLoadCsv(t *testing.T) {
|
||||
timings := `ASAP,*any,*any,*any,*any,*asap`
|
||||
destinations := ``
|
||||
rates := ``
|
||||
destinationRates := ``
|
||||
ratingPlans := ``
|
||||
ratingProfiles := ``
|
||||
sharedGroups := ``
|
||||
lcrs := ``
|
||||
actions := `TOPUP10_AC,*topup_reset,,*voice,*out,,*any,,,*unlimited,10,10,10
|
||||
DISABLE_ACNT,*disable_account,,,,,,,,,,,10
|
||||
ENABLE_ACNT,*enable_account,,,,,,,,,,,10`
|
||||
actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10`
|
||||
actionTriggers := ``
|
||||
accountActions := `cgrates.org,1,*out,TOPUP10_AT,`
|
||||
derivedCharges := ``
|
||||
cdrStats := ``
|
||||
csvr := engine.NewStringCSVReader(ratingDbAcntActs, acntDbAcntActs, ',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles,
|
||||
sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats)
|
||||
if err := csvr.LoadAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
csvr.WriteToDatabase(false, false)
|
||||
ratingDbAcntActs.CacheRating(nil, nil, nil, nil, nil)
|
||||
acntDbAcntActs.CacheAccounting(nil, nil, nil, nil)
|
||||
expectAcnt := &engine.Account{Id: "*out:cgrates.org:1"}
|
||||
if acnt, err := acntDbAcntActs.GetAccount("*out:cgrates.org:1"); err != nil {
|
||||
t.Error(err)
|
||||
} else if acnt == nil {
|
||||
t.Error("No account created")
|
||||
} else if !reflect.DeepEqual(expectAcnt, acnt) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", expectAcnt, acnt)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//ToDo
|
||||
// This test fails due to Disabled which is ignored on SetAccount with partial content. SetAccount should only be called with account when no previous one set in dataDb.
|
||||
func TestAcntActsDisableAcnt(t *testing.T) {
|
||||
acnt1Tag := "*out:cgrates.org:1"
|
||||
at := &engine.ActionTiming{
|
||||
AccountIds: []string{acnt1Tag},
|
||||
ActionsId: "DISABLE_ACNT",
|
||||
}
|
||||
if err := at.Execute(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectAcnt := &engine.Account{Id: "*out:cgrates.org:1", Disabled: true}
|
||||
if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expectAcnt, acnt) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", expectAcnt, acnt)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestAcntActsEnableAcnt(t *testing.T) {
|
||||
acnt1Tag := "*out:cgrates.org:1"
|
||||
at := &engine.ActionTiming{
|
||||
AccountIds: []string{acnt1Tag},
|
||||
ActionsId: "ENABLE_ACNT",
|
||||
}
|
||||
if err := at.Execute(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectAcnt := &engine.Account{Id: "*out:cgrates.org:1", Disabled: false}
|
||||
if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(expectAcnt, acnt) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", expectAcnt, acnt)
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ func startEngine() error {
|
||||
|
||||
func stopEngine() error {
|
||||
exec.Command("pkill", "cgr-engine").Run() // Just to make sure another one is not running, bit brutal maybe we can fine tune it
|
||||
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,14 +97,14 @@ func TestCreateCdrDirs(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
for _, cdrcDir := range []string{cfg.CdrcInstances[0].CdrInDir, cfg.CdrcInstances[0].CdrOutDir,
|
||||
*cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV2")["CDRC-CSV2"].CdrInDir, *cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV2")["CDRC-CSV2"].CdrOutDir,
|
||||
*cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV3")["CDRC-CSV3"].CdrInDir, *cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV3")["CDRC-CSV3"].CdrOutDir} {
|
||||
if err := os.RemoveAll(cdrcDir); err != nil {
|
||||
t.Fatal("Error removing folder: ", cdrcDir, err)
|
||||
}
|
||||
if err := os.MkdirAll(cdrcDir, 0755); err != nil {
|
||||
t.Fatal("Error creating folder: ", cdrcDir, err)
|
||||
for _, cdrcInst := range cfg.CdrcInstances {
|
||||
for _, dir := range []string{cdrcInst.CdrInDir, cdrcInst.CdrOutDir} {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
t.Fatal("Error removing folder: ", dir, err)
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
t.Fatal("Error creating folder: ", dir, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +115,6 @@ func TestRpcConn(t *testing.T) {
|
||||
return
|
||||
}
|
||||
startEngine()
|
||||
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
|
||||
var err error
|
||||
rater, err = jsonrpc.Dial("tcp", cfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
|
||||
if err != nil {
|
||||
|
||||
@@ -14,11 +14,13 @@ go test github.com/cgrates/cgrates/config -local
|
||||
cfg=$?
|
||||
go test github.com/cgrates/cgrates/utils -local
|
||||
utl=$?
|
||||
go test github.com/cgrates/cgrates/general_tests -local
|
||||
gnr=$?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
exit $gen && $ap1 && $ap2 && $en && $cdrc && $cfg && $utl
|
||||
exit $gen && $ap1 && $ap2 && $en && $cdrc && $cfg && $utl && $gnr
|
||||
|
||||
|
||||
Reference in New Issue
Block a user