Adding static field values for multiple mediation in configuration, fmt on sources

This commit is contained in:
DanB
2013-12-27 20:02:47 +01:00
parent 7f8f981085
commit 4eee6ddd0e
17 changed files with 189 additions and 146 deletions

View File

@@ -287,14 +287,20 @@ type AttrCacheStats struct { // Add in the future filters here maybe so we avoid
}
type CacheStats struct {
Destinations int
RatingPlans int
RatingProfiles int
Actions int
Destinations int
RatingPlans int
RatingProfiles int
Actions int
}
type AttrCachedItemAge struct {
Category string // Item's category, same name as .csv files without extension
ItemId string // Item's identity tag
Category string // Item's category, same name as .csv files without extension
ItemId string // Item's identity tag
}
type CachedItemAge struct {
Destination time.Duration
RatingPlan time.Duration
RatingProfile time.Duration
Action time.Duration
}

View File

@@ -19,10 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"net/http"
"time"
"errors"
"fmt"
"net/http"
"time"
"strings"
)
func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
@@ -105,10 +106,11 @@ func (cgrCdr CgrCdr) GetDuration() time.Duration {
}
// Used in mediation, fieldsMandatory marks whether missing field out of request represents error or can be ignored
func(cgrCdr CgrCdr) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*RatedCDR, error) {
// If the fields in parameters start with ^ their value is considered instead of dynamically retrieving it from CDR
func (cgrCdr CgrCdr) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*RatedCDR, error) {
if IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
}
var err error
var hasKey bool
var aTimeStr, durStr string
@@ -130,37 +132,57 @@ func(cgrCdr CgrCdr) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFl
if rtCdr.CdrSource, hasKey = cgrCdr[CDRSOURCE]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, CDRSOURCE))
}
if rtCdr.ReqType, hasKey = cgrCdr[reqTypeFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(reqTypeFld, STATIC_VALUE_PREFIX) { // Values starting with prefix are not dynamically populated
rtCdr.ReqType = reqTypeFld[1:]
} else if rtCdr.ReqType, hasKey = cgrCdr[reqTypeFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, reqTypeFld))
}
if rtCdr.Direction, hasKey = cgrCdr[directionFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(directionFld, STATIC_VALUE_PREFIX) {
rtCdr.Direction = directionFld[1:]
} else if rtCdr.Direction, hasKey = cgrCdr[directionFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, directionFld))
}
if rtCdr.Tenant, hasKey = cgrCdr[tenantFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(tenantFld, STATIC_VALUE_PREFIX) {
rtCdr.Tenant = tenantFld[1:]
} else if rtCdr.Tenant, hasKey = cgrCdr[tenantFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, tenantFld))
}
if rtCdr.TOR, hasKey = cgrCdr[torFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(torFld, STATIC_VALUE_PREFIX) {
rtCdr.TOR = torFld[1:]
} else if rtCdr.TOR, hasKey = cgrCdr[torFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, torFld))
}
if rtCdr.Account, hasKey = cgrCdr[accountFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(accountFld, STATIC_VALUE_PREFIX) {
rtCdr.Account = accountFld[1:]
} else if rtCdr.Account, hasKey = cgrCdr[accountFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, accountFld))
}
if rtCdr.Subject, hasKey = cgrCdr[subjectFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(subjectFld, STATIC_VALUE_PREFIX) {
rtCdr.Subject = subjectFld[1:]
} else if rtCdr.Subject, hasKey = cgrCdr[subjectFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, subjectFld))
}
if rtCdr.Destination, hasKey = cgrCdr[destFld]; !hasKey && fieldsMandatory {
if strings.HasPrefix(destFld, STATIC_VALUE_PREFIX) {
rtCdr.Destination = destFld[1:]
} else if rtCdr.Destination, hasKey = cgrCdr[destFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, destFld))
}
if aTimeStr, hasKey = cgrCdr[answerTimeFld]; !hasKey && fieldsMandatory {
if aTimeStr, hasKey = cgrCdr[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, answerTimeFld))
} else {
if strings.HasPrefix(answerTimeFld, STATIC_VALUE_PREFIX) {
aTimeStr = answerTimeFld[1:]
}
if rtCdr.AnswerTime, err = ParseTimeDetectLayout(aTimeStr); err != nil && fieldsMandatory {
return nil, err
}
}
if durStr, hasKey = cgrCdr[durationFld]; !hasKey && fieldsMandatory {
if durStr, hasKey = cgrCdr[durationFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(durationFld, STATIC_VALUE_PREFIX){
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, durationFld))
} else {
if strings.HasPrefix(durationFld, STATIC_VALUE_PREFIX) {
durStr = durationFld[1:]
}
if rtCdr.Duration, err = ParseDurationWithSecs(durStr); err != nil && fieldsMandatory {
return nil, err
}

View File

@@ -19,9 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"reflect"
"testing"
"time"
"reflect"
)
/*
@@ -80,18 +80,31 @@ func TestCgrCdrFields(t *testing.T) {
func TestCgrCdrAsRatedCdr(t *testing.T) {
cgrCdr := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
rtCdrOut, err := cgrCdr.AsRatedCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1","fieldextr2"}, true)
rtCdrOut, err := cgrCdr.AsRatedCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr := &RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746,0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1":"val_extr1", "fieldextr2": "valextr2"}, MediationRunId:"wholesale_run", Cost: -1}
expctRatedCdr := &RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
rtCdrOut2, err := cgrCdr.AsRatedCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002",
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12)*time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
_, err = cgrCdr.AsRatedCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}
}

View File

@@ -76,5 +76,6 @@ const (
DESTINATION = "destination"
ANSWER_TIME = "answer_time"
DURATION = "duration"
DEFAULT_RUNID = "default"
DEFAULT_RUNID = "default"
STATIC_VALUE_PREFIX = "^"
)

View File

@@ -22,13 +22,13 @@ import (
"crypto/rand"
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
"math"
"regexp"
"strconv"
"strings"
"time"
"regexp"
"errors"
)
// Returns first non empty string out of vals. Useful to extract defaults
@@ -113,8 +113,10 @@ func ParseTimeDetectLayout(tmStr string) (time.Time, error) {
if tmstmp, err := strconv.ParseInt(tmStr, 10, 64); err != nil {
return nilTime, err
} else {
return time.Unix(tmstmp,0), nil
return time.Unix(tmstmp, 0), nil
}
case len(tmStr) == 0: // Time probably missing from request
return nilTime, nil
}
return nilTime, errors.New("Unsupported time format")
}

View File

@@ -39,7 +39,7 @@ func NewRatedCDRFromRawCDR(rawcdr RawCDR) (*RatedCDR, error) {
if rtCdr.AnswerTime, err = rawcdr.GetAnswerTime(); err != nil {
return nil, err
}
rtCdr.Duration = time.Duration(rawcdr.GetDuration()) * time.Second
rtCdr.Duration = rawcdr.GetDuration()
rtCdr.ExtraFields = rawcdr.GetExtraFields()
rtCdr.MediationRunId = DEFAULT_RUNID
rtCdr.Cost = -1
@@ -69,59 +69,59 @@ type RatedCDR struct {
// Methods maintaining RawCDR interface
func (ratedCdr *RatedCDR) GetCgrId() string {
return ratedCdr.CgrId
return ratedCdr.CgrId
}
func (ratedCdr *RatedCDR) GetAccId() string {
return ratedCdr.AccId
return ratedCdr.AccId
}
func (ratedCdr *RatedCDR) GetCdrHost() string {
return ratedCdr.CdrHost
return ratedCdr.CdrHost
}
func (ratedCdr *RatedCDR) GetCdrSource() string {
return ratedCdr.CdrSource
return ratedCdr.CdrSource
}
func (ratedCdr *RatedCDR) GetDirection() string {
return ratedCdr.Direction
return ratedCdr.Direction
}
func (ratedCdr *RatedCDR) GetSubject() string {
return ratedCdr.Subject
return ratedCdr.Subject
}
func (ratedCdr *RatedCDR) GetAccount() string {
return ratedCdr.Account
return ratedCdr.Account
}
func (ratedCdr *RatedCDR) GetDestination() string {
return ratedCdr.Destination
return ratedCdr.Destination
}
func (ratedCdr *RatedCDR) GetTOR() string {
return ratedCdr.TOR
return ratedCdr.TOR
}
func (ratedCdr *RatedCDR) GetTenant() string {
return ratedCdr.Tenant
return ratedCdr.Tenant
}
func (ratedCdr *RatedCDR) GetReqType() string {
return ratedCdr.ReqType
return ratedCdr.ReqType
}
func (ratedCdr *RatedCDR) GetAnswerTime() (time.Time, error) {
return ratedCdr.AnswerTime, nil
return ratedCdr.AnswerTime, nil
}
func (ratedCdr *RatedCDR) GetDuration() time.Duration {
return ratedCdr.Duration
return ratedCdr.Duration
}
func (ratedCdr *RatedCDR) GetExtraFields() map[string]string {
return ratedCdr.ExtraFields
return ratedCdr.ExtraFields
}
func (ratedCdr *RatedCDR) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*RatedCDR, error) {

View File

@@ -21,6 +21,7 @@ package utils
import (
"testing"
"time"
"reflect"
)
func TestRatedCDRInterfaces(t *testing.T) {
@@ -29,11 +30,17 @@ func TestRatedCDRInterfaces(t *testing.T) {
}
func TestNewRatedCDRFromRawCDR(t *testing.T) {
cgrCdr := CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
cgrCdr := CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "internal_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
if _,err := NewRatedCDRFromRawCDR(cgrCdr); err != nil {
expctRtCdr := &RatedCDR{CgrId: FSCgrId(cgrCdr["accid"]), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
Direction: cgrCdr["direction"], Tenant: cgrCdr["tenant"], TOR: cgrCdr["tor"], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
Destination: cgrCdr["destination"], AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(10)*time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: DEFAULT_RUNID, Cost: -1}
if rt, err := NewRatedCDRFromRawCDR(cgrCdr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rt, expctRtCdr) {
t.Errorf("Received %v, expected: %v", rt, expctRtCdr)
}
}
@@ -41,7 +48,7 @@ func TestRatedCdrFields(t *testing.T) {
ratedCdr := RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0), Duration: 10,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
}
if ratedCdr.GetCgrId() != "b18944ef4dc618569f24c27b9872827a242bad0c" {
t.Error("Error parsing cdr: ", ratedCdr)
}

View File

@@ -39,6 +39,6 @@ type RawCDR interface {
GetReqType() string
GetAnswerTime() (time.Time, error)
GetDuration() time.Duration
GetExtraFields() map[string]string //Stores extra CDR Fields
GetExtraFields() map[string]string //Stores extra CDR Fields
AsRatedCdr(string, string, string, string, string, string, string, string, string, string, []string, bool) (*RatedCDR, error) // Based on fields queried will return a particular instance of RatedCDR
}

View File

@@ -157,8 +157,6 @@ func TestParseTimeDetectLayout(t *testing.T) {
t.Errorf("Expecting error")
}
}
func TestParseDateUnix(t *testing.T) {
date, err := ParseDate("1375212790")