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:
DanB
2014-05-15 12:13:03 +02:00
parent d6299cd08d
commit d0d49481a2
17 changed files with 336 additions and 119 deletions

View File

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

View File

@@ -111,6 +111,10 @@ const (
DATA = "*data"
VOICE = "*voice"
TOR = "tor"
HOURS = "hours"
MINUTES = "minutes"
NANOSECONDS = "nanoseconds"
SECONDS = "seconds"
)
var (

View File

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

View File

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

View File

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

View File

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