mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-25 17:18:44 +05:00
ApierV1.GetCdrs method, CDR fields now exported from rated_cdrs table to reflect derived charging, duration layout, added upgrade script for mysql schema
This commit is contained in:
@@ -360,6 +360,27 @@ type ExportedFileCdrs struct {
|
||||
|
||||
}
|
||||
|
||||
type AttrGetCdrs struct {
|
||||
CgrIds []string // If provided, it will filter based on the cgrids present in list
|
||||
MediationRunId []string // If provided, it will filter on mediation runid
|
||||
TOR []string // If provided, filter on TypeOfRecord
|
||||
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
|
||||
Category []string // If provided, it will filter çategory
|
||||
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
|
||||
OrderIdStart int64 // Export from this order identifier
|
||||
OrderIdEnd int64 // Export smaller than this order identifier
|
||||
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
|
||||
}
|
||||
|
||||
@@ -111,6 +111,10 @@ const (
|
||||
DATA = "*data"
|
||||
VOICE = "*voice"
|
||||
TOR = "tor"
|
||||
HOURS = "hours"
|
||||
MINUTES = "minutes"
|
||||
NANOSECONDS = "nanoseconds"
|
||||
SECONDS = "seconds"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -201,6 +201,13 @@ func ParseDurationWithSecs(durStr string) (time.Duration, error) {
|
||||
return time.ParseDuration(durStr)
|
||||
}
|
||||
|
||||
func ParseDurationWithNanosecs(durStr string) (time.Duration, error) {
|
||||
if _, err := strconv.Atoi(durStr); err == nil { // No suffix, default to seconds
|
||||
durStr += "ns"
|
||||
}
|
||||
return time.ParseDuration(durStr)
|
||||
}
|
||||
|
||||
func BalanceKey(tenant, account, direction string) string {
|
||||
return fmt.Sprintf("%s:%s:%s", direction, tenant, account)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,19 @@ func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string
|
||||
return strconv.FormatFloat(cost, 'f', roundDecimals, 64)
|
||||
}
|
||||
|
||||
func (storedCdr *StoredCdr) FormatDuration(layout string) string {
|
||||
switch layout {
|
||||
case HOURS:
|
||||
return strconv.FormatFloat(storedCdr.Duration.Hours(), 'f', -1, 64)
|
||||
case MINUTES:
|
||||
return strconv.FormatFloat(storedCdr.Duration.Minutes(), 'f', -1, 64)
|
||||
case SECONDS:
|
||||
return strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64)
|
||||
default:
|
||||
return strconv.FormatInt(storedCdr.Duration.Nanoseconds(), 10)
|
||||
}
|
||||
}
|
||||
|
||||
// Used to retrieve fields as string, primary fields are const labeled
|
||||
func (storedCdr *StoredCdr) FieldAsString(rsrFld *RSRField) string {
|
||||
switch rsrFld.Id {
|
||||
@@ -93,7 +106,7 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *RSRField) string {
|
||||
case ANSWER_TIME:
|
||||
return rsrFld.ParseValue(storedCdr.AnswerTime.String())
|
||||
case DURATION:
|
||||
return rsrFld.ParseValue(strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
|
||||
return rsrFld.ParseValue(strconv.FormatInt(storedCdr.Duration.Nanoseconds(), 10))
|
||||
case MEDI_RUNID:
|
||||
return rsrFld.ParseValue(storedCdr.MediationRunId)
|
||||
case COST:
|
||||
@@ -126,7 +139,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values {
|
||||
v.Set(DESTINATION, storedCdr.Destination)
|
||||
v.Set(SETUP_TIME, storedCdr.SetupTime.String())
|
||||
v.Set(ANSWER_TIME, storedCdr.AnswerTime.String())
|
||||
v.Set(DURATION, strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
|
||||
v.Set(DURATION, strconv.FormatInt(storedCdr.Duration.Nanoseconds(), 10))
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -216,7 +229,7 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena
|
||||
durStr := storedCdr.FieldAsString(durationFld)
|
||||
if primaryMandatory && len(durStr) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, DURATION, durationFld.Id))
|
||||
} else if frkStorCdr.Duration, err = ParseDurationWithSecs(durStr); err != nil {
|
||||
} else if frkStorCdr.Duration, err = ParseDurationWithNanosecs(durStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds))
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestFieldAsString(t *testing.T) {
|
||||
cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination ||
|
||||
cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String() ||
|
||||
cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String() ||
|
||||
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10" ||
|
||||
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10000000000" ||
|
||||
cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId ||
|
||||
cdr.FieldAsString(&RSRField{Id: COST}) != "1.01" ||
|
||||
cdr.FieldAsString(&RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"] ||
|
||||
@@ -69,7 +69,7 @@ func TestFieldAsString(t *testing.T) {
|
||||
cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination,
|
||||
cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String(),
|
||||
cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String(),
|
||||
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10",
|
||||
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10000000000",
|
||||
cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId,
|
||||
cdr.FieldAsString(&RSRField{Id: COST}) != "1.01",
|
||||
cdr.FieldAsString(&RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"],
|
||||
@@ -98,6 +98,16 @@ func TestFormatCost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatDuration(t *testing.T) {
|
||||
cdr := StoredCdr{Duration: time.Duration(10) * time.Second}
|
||||
if cdr.FormatDuration(SECONDS) != "10" {
|
||||
t.Error("Wrong duration format: ", cdr.FormatDuration(SECONDS))
|
||||
}
|
||||
if cdr.FormatDuration("default") != "10000000000" {
|
||||
t.Error("Wrong duration format: ", cdr.FormatDuration("default"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrAsHttpForm(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
|
||||
CdrSource: UNIT_TEST, ReqType: "rated", Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
|
||||
@@ -144,8 +154,8 @@ func TestStoredCdrAsHttpForm(t *testing.T) {
|
||||
if cdrForm.Get(ANSWER_TIME) != "2013-11-07 08:42:26 +0000 UTC" {
|
||||
t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:26 +0000 UTC", cdrForm.Get(ANSWER_TIME))
|
||||
}
|
||||
if cdrForm.Get(DURATION) != "10" {
|
||||
t.Errorf("Expected: %s, received: %s", "10", cdrForm.Get(DURATION))
|
||||
if cdrForm.Get(DURATION) != "10000000000" {
|
||||
t.Errorf("Expected: %s, received: %s", "10000000000", cdrForm.Get(DURATION))
|
||||
}
|
||||
if cdrForm.Get("field_extr1") != "val_extr1" {
|
||||
t.Errorf("Expected: %s, received: %s", "val_extr1", cdrForm.Get("field_extr1"))
|
||||
|
||||
@@ -375,6 +375,30 @@ func TestParseDurationWithSecs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDurationWithNanosecs(t *testing.T) {
|
||||
durStr := "2"
|
||||
durExpected := time.Duration(2) * time.Nanosecond
|
||||
if parsed, err := ParseDurationWithNanosecs(durStr); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsed != durExpected {
|
||||
t.Error("Parsed different than expected")
|
||||
}
|
||||
durStr = "2s"
|
||||
durExpected = time.Duration(2) * time.Second
|
||||
if parsed, err := ParseDurationWithNanosecs(durStr); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsed != durExpected {
|
||||
t.Error("Parsed different than expected")
|
||||
}
|
||||
durStr = "2ms"
|
||||
durExpected = time.Duration(2) * time.Millisecond
|
||||
if parsed, err := ParseDurationWithNanosecs(durStr); err != nil {
|
||||
t.Error(err)
|
||||
} else if parsed != durExpected {
|
||||
t.Error("Parsed different than expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinDuration(t *testing.T) {
|
||||
d1, _ := time.ParseDuration("1m")
|
||||
d2, _ := time.ParseDuration("59s")
|
||||
|
||||
Reference in New Issue
Block a user