mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Adding detailed CDR export filters both in storage and APIs
This commit is contained in:
@@ -21,6 +21,7 @@ package apier
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cgrates/cgrates/cdrexporter"
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"os"
|
||||
"path"
|
||||
@@ -35,6 +36,12 @@ 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
|
||||
@@ -45,7 +52,8 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
|
||||
return err
|
||||
}
|
||||
}
|
||||
cdrs, err := self.CdrDb.GetStoredCdrs(tStart, tEnd, attr.SkipErrors, attr.SkipRated)
|
||||
cdrs, err := self.CdrDb.GetStoredCdrs(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
|
||||
}
|
||||
@@ -58,7 +66,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
|
||||
} else {
|
||||
defer fileOut.Close()
|
||||
}
|
||||
csvWriter := cdrexporter.NewCsvCdrWriter(fileOut, self.Config.RoundingDecimals, self.Config.CdreExportedFields)
|
||||
csvWriter := cdrexporter.NewCsvCdrWriter(fileOut, self.Config.RoundingDecimals, exportedFields)
|
||||
for _, cdr := range cdrs {
|
||||
if err := csvWriter.Write(cdr); err != nil {
|
||||
os.Remove(fileName)
|
||||
@@ -102,7 +102,7 @@ type CdrStorage interface {
|
||||
Storage
|
||||
SetCdr(utils.RawCDR) error
|
||||
SetRatedCdr(*utils.StoredCdr, string) error
|
||||
GetStoredCdrs(time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
|
||||
GetStoredCdrs(string, string, string, string, string, string, string, string, string, string, time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
|
||||
RemStoredCdrs([]string) error
|
||||
}
|
||||
|
||||
|
||||
@@ -569,10 +569,71 @@ 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(timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
|
||||
func (self *SQLStorage) GetStoredCdrs(runId, 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(runId) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" runid='%s'", runId)
|
||||
}
|
||||
if len(cdrHost) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" cdrhost='%s'", cdrHost)
|
||||
}
|
||||
if len(cdrSource) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" cdrsource='%s'", cdrSource)
|
||||
}
|
||||
if len(reqType) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" reqtype='%s'", reqType)
|
||||
}
|
||||
if len(direction) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" direction='%s'", direction)
|
||||
}
|
||||
if len(tenant) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" tenant='%s'", tenant)
|
||||
}
|
||||
if len(tor) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" tor='%s'", tor)
|
||||
}
|
||||
if len(account) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" account='%s'", account)
|
||||
}
|
||||
if len(subject) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" %s.subject='%s'", utils.TBL_CDRS_PRIMARY, subject)
|
||||
}
|
||||
if len(destPrefix) != 0 {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
}
|
||||
fltr += fmt.Sprintf(" destination LIKE '%s%'", destPrefix)
|
||||
}
|
||||
if !timeStart.IsZero() {
|
||||
if len(fltr) != 0 {
|
||||
fltr += " AND "
|
||||
|
||||
@@ -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("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, !rerateErrors, !rerateRated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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("", "", "", "", "", "", "", "", "", "", 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)))
|
||||
|
||||
@@ -309,12 +309,23 @@ type CachedItemAge struct {
|
||||
}
|
||||
|
||||
type AttrExpFileCdrs struct {
|
||||
CdrFormat string // Cdr output file format <utils.CdreCdrFormats>
|
||||
TimeStart string // If provided, will represent the starting of the CDRs interval (>=)
|
||||
TimeEnd string // If provided, 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>
|
||||
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
|
||||
}
|
||||
|
||||
type ExportedFileCdrs struct {
|
||||
|
||||
Reference in New Issue
Block a user