API for CdrExporter fixed_width, RemCdr separated from exporter due to buggy scenario discovered

This commit is contained in:
DanB
2014-03-24 22:23:03 +01:00
parent 881db9c1c4
commit f390281f41
13 changed files with 176 additions and 92 deletions

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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")
}

View File

@@ -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("<element>"), cfgInst.RawConfig...) // Encapsulate the rawConfig in one element so we can Unmarshall into one struct
rawConfig = append(rawConfig, []byte("</element>")...)

View File

@@ -44,7 +44,7 @@ func TestParseXmlConfig(t *testing.T) {
<content>
<fields>
<field name="RecordType" type="constant" value="20" width="2"/>
<field name="SIPTrunkID" type="cdrfield" value="cgrid" width="12"/>
<field name="SIPTrunkID" type="cdrfield" value="cgrid" width="12" mandatory="true"/>
<field name="ConnectionNumber" type="cdrfield" value="subject" strip="left" padding="left" width="5"/>
<field name="ANumber" type="cdrfield" value="cli" strip="xright" width="15"/>
<field name="CalledNumber" type="cdrfield" value="destination" strip="xright" width="24"/>

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)))

View File

@@ -316,23 +316,30 @@ type CachedItemAge struct {
}
type AttrExpFileCdrs struct {
CdrFormat string // Cdr output file format <utils.CdreCdrFormats>
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 <utils.CdreCdrFormats>
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 {

View File

@@ -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}
)