diff --git a/apier/cdre.go b/apier/cdre.go
index 110e04bc0..ba29fa0e6 100644
--- a/apier/cdre.go
+++ b/apier/cdre.go
@@ -25,10 +25,12 @@ import (
"github.com/cgrates/cgrates/utils"
"os"
"path"
+ "strconv"
"strings"
"time"
)
+// Export Cdrs to file
func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.ExportedFileCdrs) error {
var tStart, tEnd time.Time
var err error
@@ -36,12 +38,6 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
if !utils.IsSliceMember(utils.CdreCdrFormats, cdrFormat) {
return fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "CdrFormat")
}
- exportedFields := self.Config.CdreExportedFields
- if len(attr.ExportedFields) != 0 {
- if exportedFields, err = config.ParseRSRFields(attr.ExportedFields); err != nil {
- return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
- }
- }
if len(attr.TimeStart) != 0 {
if tStart, err = utils.ParseTimeDetectLayout(attr.TimeStart); err != nil {
return err
@@ -52,38 +48,93 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
return err
}
}
- cdrs, err := self.CdrDb.GetStoredCdrs(attr.MediationRunId, attr.CdrHost, attr.CdrSource, attr.ReqType, attr.Direction,
+ fileName := attr.ExportFileName
+ exportId := attr.ExportId
+ if len(exportId) == 0 {
+ exportId = strconv.FormatInt(time.Now().Unix(), 10)
+ }
+ roundDecimals := attr.RoundingDecimals
+ if roundDecimals == 0 {
+ roundDecimals = self.Config.RoundingDecimals
+ }
+ cdrs, err := self.CdrDb.GetStoredCdrs(attr.CgrIds, attr.MediationRunId, attr.CdrHost, attr.CdrSource, attr.ReqType, attr.Direction,
attr.Tenant, attr.Tor, attr.Account, attr.Subject, attr.DestinationPrefix, tStart, tEnd, attr.SkipErrors, attr.SkipRated)
if err != nil {
return err
+ } else if len(cdrs) == 0 {
+ *reply = utils.ExportedFileCdrs{"", 0}
+ return nil
}
- var fileName string
- if cdrFormat == utils.CDRE_CSV && len(cdrs) != 0 {
- fileName = path.Join(self.Config.CdreDir, fmt.Sprintf("cdrs_%d.csv", time.Now().Unix()))
- fileOut, err := os.Create(fileName)
+ switch cdrFormat {
+ case utils.CDRE_CSV:
+ if len(fileName) == 0 {
+ fileName = fmt.Sprintf("cdre_%s.csv", exportId)
+ }
+ exportedFields := self.Config.CdreExportedFields
+ if len(attr.ExportTemplate) != 0 {
+ if exportedFields, err = config.ParseRSRFields(attr.ExportTemplate); err != nil {
+ return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
+ }
+ }
+ if len(exportedFields) == 0 {
+ return fmt.Errorf("%s:ExportTemplate", utils.ERR_MANDATORY_IE_MISSING)
+ }
+ filePath := path.Join(self.Config.CdreDir, fileName)
+ fileOut, err := os.Create(filePath)
if err != nil {
return err
- } else {
- defer fileOut.Close()
}
- csvWriter := cdre.NewCsvCdrWriter(fileOut, self.Config.RoundingDecimals, exportedFields)
+ defer fileOut.Close()
+ csvWriter := cdre.NewCsvCdrWriter(fileOut, roundDecimals, exportedFields)
for _, cdr := range cdrs {
if err := csvWriter.WriteCdr(cdr); err != nil {
- os.Remove(fileName)
+ os.Remove(filePath)
return err
}
}
csvWriter.Close()
- if attr.RemoveFromDb {
- cgrIds := make([]string, len(cdrs))
- for idx, cdr := range cdrs {
- cgrIds[idx] = cdr.CgrId
+ case utils.CDRE_FIXED_WIDTH:
+ if len(fileName) == 0 {
+ fileName = fmt.Sprintf("cdre_%s.fwv", exportId)
+ }
+ exportTemplate := self.Config.CdreFWXmlTemplate
+ if len(attr.ExportTemplate) != 0 && self.Config.XmlCfgDocument != nil {
+ if xmlTemplate, err := self.Config.XmlCfgDocument.GetCdreFWCfg(attr.ExportTemplate[len(utils.XML_PROFILE_PREFIX):]); err != nil {
+ return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
+ } else if xmlTemplate != nil {
+ exportTemplate = xmlTemplate
}
- if err := self.CdrDb.RemStoredCdrs(cgrIds); err != nil {
+ }
+ if exportTemplate == nil {
+ return fmt.Errorf("%s:ExportTemplate", utils.ERR_MANDATORY_IE_MISSING)
+ }
+ filePath := path.Join(self.Config.CdreDir, fileName)
+ fileOut, err := os.Create(filePath)
+ if err != nil {
+ return err
+ }
+ defer fileOut.Close()
+ fww, _ := cdre.NewFWCdrWriter(self.LogDb, fileOut, exportTemplate, exportId, roundDecimals)
+ for _, cdr := range cdrs {
+ if err := fww.WriteCdr(cdr); err != nil {
+ os.Remove(filePath)
return err
}
}
+ fww.Close()
}
*reply = utils.ExportedFileCdrs{fileName, len(cdrs)}
return nil
}
+
+// Remove Cdrs out of CDR storage
+func (self *ApierV1) RemCdrs(attrs utils.AttrRemCdrs, reply *string) error {
+ if len(attrs.CgrIds) == 0 {
+ return fmt.Errorf("%s:CgrIds", utils.ERR_MANDATORY_IE_MISSING)
+ }
+ if err := self.CdrDb.RemStoredCdrs(attrs.CgrIds); err != nil {
+ return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
+ }
+ *reply = "OK"
+ return nil
+}
diff --git a/cdre/fixedwidth.go b/cdre/fixedwidth.go
index 6f5b00b1a..f5a10ce56 100644
--- a/cdre/fixedwidth.go
+++ b/cdre/fixedwidth.go
@@ -27,6 +27,7 @@ import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"io"
+ "os"
"strconv"
"strings"
"time"
@@ -50,12 +51,23 @@ const (
var err error
+func NewFWCdrWriter(logDb engine.LogStorage, outFile *os.File, exportTpl *config.CgrXmlCdreFwCfg, exportId string, roundDecimals int) (*FixedWidthCdrWriter, error) {
+ return &FixedWidthCdrWriter{
+ logDb: logDb,
+ writer: outFile,
+ exportTemplate: exportTpl,
+ exportId: exportId,
+ roundDecimals: roundDecimals,
+ header: &bytes.Buffer{},
+ content: &bytes.Buffer{},
+ trailer: &bytes.Buffer{}}, nil
+}
+
type FixedWidthCdrWriter struct {
logDb engine.LogStorage // Used to extract cost_details if these are requested
writer io.Writer
exportTemplate *config.CgrXmlCdreFwCfg
exportId string // Unique identifier or this export
- exportFileName string // If defined it will overwrite the file name
roundDecimals int
header, content, trailer *bytes.Buffer
firstCdrTime, lastCdrTime time.Time
diff --git a/config/config.go b/config/config.go
index 82b69a1bd..ddfa8c395 100644
--- a/config/config.go
+++ b/config/config.go
@@ -283,7 +283,7 @@ func (self *CGRConfig) setDefaults() error {
func (self *CGRConfig) checkConfigSanity() error {
// Cdre sanity check for fixed_width
- if self.CdreCdrFormat == utils.FIXED_WIDTH {
+ if self.CdreCdrFormat == utils.CDRE_FIXED_WIDTH {
if self.XmlCfgDocument == nil {
return errors.New("Need XmlConfigurationDocument for fixed_width cdr export")
} else if self.CdreFWXmlTemplate == nil {
@@ -492,7 +492,7 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
}
if hasOpt = c.HasOption("cdre", "export_template"); hasOpt { // Load configs for csv normally from template, fixed_width from xml file
exportTemplate, _ := c.GetString("cdre", "export_template")
- if cfg.CdreCdrFormat != utils.FIXED_WIDTH { // Csv most likely
+ if cfg.CdreCdrFormat != utils.CDRE_FIXED_WIDTH { // Csv most likely
if extraFields, err := ParseRSRFields(exportTemplate); err != nil {
return nil, errParse
} else {
diff --git a/config/config_test.go b/config/config_test.go
index a528c3f8d..e9b83c4c9 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -184,7 +184,7 @@ func TestSanityCheck(t *testing.T) {
t.Error("Failed to detect config insanity")
}
cfg = &CGRConfig{}
- cfg.CdreCdrFormat = utils.FIXED_WIDTH
+ cfg.CdreCdrFormat = utils.CDRE_FIXED_WIDTH
if err := cfg.checkConfigSanity(); err == nil {
t.Error("Failed to detect fixed_width dependency on xml configuration")
}
diff --git a/config/xmlconfig.go b/config/xmlconfig.go
index 1ec79ec93..289375620 100644
--- a/config/xmlconfig.go
+++ b/config/xmlconfig.go
@@ -83,18 +83,18 @@ type CgrXmlCfgCdrField struct {
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:"layout,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported
+ 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
}
// Avoid building from raw config string always, so build cache here
func (xmlCfg *CgrXmlCfgDocument) cacheCdreFWCfgs() error {
xmlCfg.cdrefws = make(map[string]*CgrXmlCdreFwCfg)
for _, cfgInst := range xmlCfg.Configurations {
- if cfgInst.Section == utils.CDRE || cfgInst.Type == utils.FIXED_WIDTH {
+ if cfgInst.Section == utils.CDRE || cfgInst.Type == utils.CDRE_FIXED_WIDTH {
cdrefwCfg := new(CgrXmlCdreFwCfg)
rawConfig := append([]byte(""), cfgInst.RawConfig...) // Encapsulate the rawConfig in one element so we can Unmarshall into one struct
rawConfig = append(rawConfig, []byte("")...)
diff --git a/config/xmlconfig_test.go b/config/xmlconfig_test.go
index 43de27cd3..56ee6c43c 100644
--- a/config/xmlconfig_test.go
+++ b/config/xmlconfig_test.go
@@ -44,7 +44,7 @@ func TestParseXmlConfig(t *testing.T) {
-
+
diff --git a/engine/storage_interface.go b/engine/storage_interface.go
index 10298939d..8a7f4c0a4 100644
--- a/engine/storage_interface.go
+++ b/engine/storage_interface.go
@@ -106,7 +106,7 @@ type CdrStorage interface {
Storage
SetCdr(utils.RawCDR) error
SetRatedCdr(*utils.StoredCdr, string) error
- GetStoredCdrs(string, string, string, string, string, string, string, string, string, string, time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
+ GetStoredCdrs([]string, string, string, string, string, string, string, string, string, string, string, time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
RemStoredCdrs([]string) error
}
diff --git a/engine/storage_sql.go b/engine/storage_sql.go
index 839f714df..2accf8dcc 100644
--- a/engine/storage_sql.go
+++ b/engine/storage_sql.go
@@ -577,97 +577,111 @@ func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string
// Return a slice of CDRs from storDb using optional filters.a
// ignoreErr - do not consider cdrs with rating errors
// ignoreRated - do not consider cdrs which were already rated, including here the ones with errors
-func (self *SQLStorage) GetStoredCdrs(runId, cdrHost, cdrSource, reqType, direction, tenant, tor, account, subject, destPrefix string,
+func (self *SQLStorage) GetStoredCdrs(cgrIds []string, runId string, cdrHost, cdrSource, reqType, direction, tenant, tor, account, subject, destPrefix string,
timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
var cdrs []*utils.StoredCdr
q := fmt.Sprintf("SELECT %s.cgrid,accid,cdrhost,cdrsource,reqtype,direction,tenant,tor,account,%s.subject,destination,setup_time,answer_time,duration,extra_fields,runid,cost FROM %s LEFT JOIN %s ON %s.cgrid=%s.cgrid LEFT JOIN %s ON %s.cgrid=%s.cgrid", utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_RATED_CDRS, utils.TBL_CDRS_PRIMARY, utils.TBL_RATED_CDRS)
fltr := ""
+ if len(cgrIds) != 0 {
+ qIds := " ("
+ for idxId, cgrId := range cgrIds {
+ if idxId != 0 {
+ qIds += " OR"
+ }
+ qIds += fmt.Sprintf(" %s.cgrid='%s'", utils.TBL_CDRS_PRIMARY, cgrId)
+ }
+ qIds += " )"
+ if len(fltr) != 0 {
+ fltr += " AND"
+ }
+ fltr += qIds
+ }
if len(runId) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" runid='%s'", runId)
}
if len(cdrHost) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" cdrhost='%s'", cdrHost)
}
if len(cdrSource) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" cdrsource='%s'", cdrSource)
}
if len(reqType) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" reqtype='%s'", reqType)
}
if len(direction) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" direction='%s'", direction)
}
if len(tenant) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" tenant='%s'", tenant)
}
if len(tor) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" tor='%s'", tor)
}
if len(account) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" account='%s'", account)
}
if len(subject) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" %s.subject='%s'", utils.TBL_CDRS_PRIMARY, subject)
}
if len(destPrefix) != 0 {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" destination LIKE '%s%%'", destPrefix)
}
if !timeStart.IsZero() {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" answer_time>='%s'", timeStart)
}
if !timeEnd.IsZero() {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
fltr += fmt.Sprintf(" answer_time<'%s'", timeEnd)
}
if ignoreRated {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
if ignoreErr {
- fltr += "cost IS NULL"
+ fltr += " cost IS NULL"
} else {
- fltr += "(cost=-1 OR cost IS NULL)"
+ fltr += " (cost=-1 OR cost IS NULL)"
}
} else if ignoreErr {
if len(fltr) != 0 {
- fltr += " AND "
+ fltr += " AND"
}
- fltr += "(cost!=-1 OR cost IS NULL)"
+ fltr += " (cost!=-1 OR cost IS NULL)"
}
if len(fltr) != 0 {
q += fmt.Sprintf(" WHERE %s", fltr)
diff --git a/engine/storage_sql_local_test.go b/engine/storage_sql_local_test.go
index 06b8dcd89..88c66aabf 100644
--- a/engine/storage_sql_local_test.go
+++ b/engine/storage_sql_local_test.go
@@ -213,93 +213,93 @@ func TestGetStoredCdrs(t *testing.T) {
}
var timeStart, timeEnd time.Time
// All CDRs, no filter
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 8 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on runId
- if storedCdrs, err := mysql.GetStoredCdrs(utils.DEFAULT_RUNID, "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, utils.DEFAULT_RUNID, "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 2 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on cdrHost
- if storedCdrs, err := mysql.GetStoredCdrs("", "192.168.1.2", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "192.168.1.2", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 3 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on cdrSource
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "UNKNOWN", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "UNKNOWN", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 1 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on reqType
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "prepaid", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "prepaid", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 2 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on direction
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "*out", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "*out", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 8 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on tenant
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "itsyscom.com", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "itsyscom.com", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 3 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on tor
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "premium_call", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "premium_call", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 1 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on account
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "1002", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "1002", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 3 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on subject
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "1000", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "1000", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 1 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on ignoreErr
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, true, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, true, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 8 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on ignoreRated
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, true); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, true); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 5 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on timeStart
timeStart = time.Date(2013, 11, 8, 8, 0, 0, 0, time.UTC)
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 5 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Filter on timeStart and timeEnd
timeEnd = time.Date(2013, 12, 1, 8, 0, 0, 0, time.UTC)
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 2 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
// Combined filter
- if storedCdrs, err := mysql.GetStoredCdrs("", "", "", "rated", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
+ if storedCdrs, err := mysql.GetStoredCdrs(nil, "", "", "", "rated", "", "", "", "", "", "", timeStart, timeEnd, false, false); err != nil {
t.Error(err.Error())
} else if len(storedCdrs) != 1 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
diff --git a/mediator/mediator.go b/mediator/mediator.go
index 47781299b..229ae6b46 100644
--- a/mediator/mediator.go
+++ b/mediator/mediator.go
@@ -161,7 +161,7 @@ func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error {
}
func (self *Mediator) RateCdrs(timeStart, timeEnd time.Time, rerateErrors, rerateRated bool) error {
- cdrs, err := self.cdrDb.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, !rerateErrors, !rerateRated)
+ cdrs, err := self.cdrDb.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, !rerateErrors, !rerateRated)
if err != nil {
return err
}
diff --git a/mediator/mediator_local_test.go b/mediator/mediator_local_test.go
index 771d75a56..b2616986b 100644
--- a/mediator/mediator_local_test.go
+++ b/mediator/mediator_local_test.go
@@ -150,12 +150,12 @@ func TestPostCdrs(t *testing.T) {
}
}
time.Sleep(10 * time.Millisecond) // Give time for CDRs to reach database
- if storedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
+ if storedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
t.Error(err)
} else if len(storedCdrs) != 2 { // Make sure CDRs made it into StorDb
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(storedCdrs)))
}
- if nonErrorCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, false); err != nil {
+ if nonErrorCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, false); err != nil {
t.Error(err)
} else if len(nonErrorCdrs) != 0 { // Just two of them should be without errors
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(nonErrorCdrs)))
@@ -178,12 +178,12 @@ func TestInjectCdrs(t *testing.T) {
t.Error(err)
}
}
- if storedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
+ if storedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
t.Error(err)
} else if len(storedCdrs) != 4 { // Make sure CDRs made it into StorDb
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(storedCdrs)))
}
- if nonRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
+ if nonRatedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
t.Error(err)
} else if len(nonRatedCdrs) != 2 { // Just two of them should be non-rated
t.Error(fmt.Sprintf("Unexpected number of CDRs non-rated: %d", len(nonRatedCdrs)))
@@ -215,12 +215,12 @@ func TestRateCdrs(t *testing.T) {
} else if reply != utils.OK {
t.Errorf("Unexpected reply: %s", reply)
}
- if nonRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
+ if nonRatedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
t.Error(err)
} else if len(nonRatedCdrs) != 0 { // Just two of them should be non-rated
t.Error(fmt.Sprintf("Unexpected number of CDRs non-rated: %d", len(nonRatedCdrs)))
}
- if errRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
+ if errRatedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
t.Error(err)
} else if len(errRatedCdrs) != 2 { // The first 2 with errors should be still there before rerating
t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs)))
@@ -230,7 +230,7 @@ func TestRateCdrs(t *testing.T) {
} else if reply != utils.OK {
t.Errorf("Unexpected reply: %s", reply)
}
- if errRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
+ if errRatedCdrs, err := cdrStor.GetStoredCdrs(nil, "", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
t.Error(err)
} else if len(errRatedCdrs) != 1 { // One CDR with errors should be fixed now by rerating
t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs)))
diff --git a/utils/apitpdata.go b/utils/apitpdata.go
index bdfddcc88..469b535fe 100644
--- a/utils/apitpdata.go
+++ b/utils/apitpdata.go
@@ -316,23 +316,30 @@ type CachedItemAge struct {
}
type AttrExpFileCdrs struct {
- CdrFormat string // Cdr output file format
- ExportedFields string // Optional comma separated list of fields ot be exported in a CDR
- MediationRunId string // If provided, it will filter on mediation runid
- CdrHost string // If provided, it will filter cdrhost
- CdrSource string // If provided, it will filter cdrsource
- ReqType string // If provided, it will fiter reqtype
- Direction string // If provided, it will fiter direction
- Tenant string // If provided, it will filter tenant
- Tor string // If provided, it will filter tor
- Account string // If provided, it will filter account
- Subject string // If provided, it will filter the rating subject
- DestinationPrefix string // If provided, it will filter on destination prefix
- TimeStart string // If provided, it will represent the starting of the CDRs interval (>=)
- TimeEnd string // If provided, it will represent the end of the CDRs interval (<)
- SkipErrors bool // Do not export errored CDRs
- SkipRated bool // Do not export rated CDRs
- RemoveFromDb bool // If true the CDRs will be also deleted after export
+ CdrFormat string // Cdr output file format
+ ExportId string // Optional exportid
+ ExportFileName string // If provided the output filename will be set to this
+ ExportTemplate string // Exported fields template <""|fld1,fld2|*xml:instance_name>
+ RoundingDecimals int // Overwrite configured roundDecimals with this dynamically
+ CgrIds []string // If provided, it will filter based on the cgrids present in list
+ MediationRunId string // If provided, it will filter on mediation runid
+ CdrHost string // If provided, it will filter cdrhost
+ CdrSource string // If provided, it will filter cdrsource
+ ReqType string // If provided, it will fiter reqtype
+ Direction string // If provided, it will fiter direction
+ Tenant string // If provided, it will filter tenant
+ Tor string // If provided, it will filter tor
+ Account string // If provided, it will filter account
+ Subject string // If provided, it will filter the rating subject
+ DestinationPrefix string // If provided, it will filter on destination prefix
+ TimeStart string // If provided, it will represent the starting of the CDRs interval (>=)
+ TimeEnd string // If provided, it will represent the end of the CDRs interval (<)
+ SkipErrors bool // Do not export errored CDRs
+ SkipRated bool // Do not export rated CDRs
+}
+
+type AttrRemCdrs struct {
+ CgrIds []string // List of CgrIds to remove from storeDb
}
type ExportedFileCdrs struct {
diff --git a/utils/consts.go b/utils/consts.go
index 2f572e37d..f9d2d133b 100644
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -91,11 +91,11 @@ const (
INTERNAL = "internal"
ZERO_RATING_SUBJECT_PREFIX = "*zero"
OK = "OK"
- FIXED_WIDTH = "fixed_width"
+ CDRE_FIXED_WIDTH = "fixed_width"
XML_PROFILE_PREFIX = "*xml:"
CDRE = "cdre"
)
var (
- CdreCdrFormats = []string{CDRE_CSV, CDRE_DRYRUN}
+ CdreCdrFormats = []string{CDRE_CSV, CDRE_DRYRUN, CDRE_FIXED_WIDTH}
)