Synched Http CDR with CDR object

This commit is contained in:
porosnicuadrian
2020-11-02 18:02:08 +02:00
committed by Dan Christian Bogos
parent 1a6d0dbc4a
commit c408254743
4 changed files with 187 additions and 51 deletions

View File

@@ -33,15 +33,20 @@ var cdrServer *CDRServer // Share the server so we can use it in http handlers
// cgrCdrHandler handles CDRs received over HTTP REST
func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
cgrCdr, err := NewCgrCdrFromHttpReq(r,
cdrServer.cgrCfg.GeneralCfg().DefaultTimezone)
cgrCdr, err := NewCgrCdrFromHttpReq(r)
if err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> could not create CDR entry from http: %+v, err <%s>",
utils.CDRs, r.Form, err.Error()))
return
}
cdr := cgrCdr.AsCDR(cdrServer.cgrCfg.GeneralCfg().DefaultTimezone)
cdr, err := cgrCdr.AsCDR(cdrServer.cgrCfg.GeneralCfg().DefaultTimezone)
if err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> could not create CDR entry from rawCDR: %+v, err <%s>",
utils.CDRs, cgrCdr, err.Error()))
return
}
var ignored string
if err := cdrServer.V1ProcessCDR(&CDRWithOpts{CDR: cdr}, &ignored); err != nil {
utils.Logger.Warning(

View File

@@ -19,13 +19,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"encoding/json"
"net/http"
"strconv"
"github.com/cgrates/cgrates/utils"
)
func NewCgrCdrFromHttpReq(req *http.Request, timezone string) (CgrCdr, error) {
func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
if req.Form == nil {
if err := req.ParseForm(); err != nil {
return nil, err
@@ -41,7 +42,7 @@ func NewCgrCdrFromHttpReq(req *http.Request, timezone string) (CgrCdr, error) {
type CgrCdr map[string]string
func (cgrCdr CgrCdr) getCGRID(timezone string) string {
func (cgrCdr CgrCdr) getCGRID() string {
if CGRID, hasIt := cgrCdr[utils.CGRID]; hasIt {
return CGRID
}
@@ -58,29 +59,61 @@ func (cgrCdr CgrCdr) getExtraFields() map[string]string {
return extraFields
}
func (cgrCdr CgrCdr) AsCDR(timezone string) *CDR {
storCdr := new(CDR)
storCdr.CGRID = cgrCdr.getCGRID(timezone)
storCdr.ToR = cgrCdr[utils.ToR]
storCdr.OriginID = cgrCdr[utils.OriginID]
storCdr.OriginHost = cgrCdr[utils.OriginHost]
storCdr.Source = cgrCdr[utils.Source]
storCdr.RequestType = cgrCdr[utils.RequestType]
storCdr.Tenant = cgrCdr[utils.Tenant]
storCdr.Category = cgrCdr[utils.Category]
storCdr.Account = cgrCdr[utils.Account]
storCdr.Subject = cgrCdr[utils.Subject]
storCdr.Destination = cgrCdr[utils.Destination]
storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SetupTime], timezone) // Not interested to process errors, should do them if necessary in a previous step
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.AnswerTime], timezone)
storCdr.Usage, _ = utils.ParseDurationWithNanosecs(cgrCdr[utils.Usage])
storCdr.ExtraFields = cgrCdr.getExtraFields()
func (cgrCdr CgrCdr) AsCDR(timezone string) (storCdr *CDR, err error) {
storCdr = &CDR{
CGRID: cgrCdr.getCGRID(),
RunID: cgrCdr[utils.RunID],
OriginHost: cgrCdr[utils.OriginHost],
Source: cgrCdr[utils.Source],
OriginID: cgrCdr[utils.OriginID],
ToR: cgrCdr[utils.ToR],
RequestType: cgrCdr[utils.RequestType],
Tenant: cgrCdr[utils.Tenant],
Category: cgrCdr[utils.Category],
Account: cgrCdr[utils.Account],
Subject: cgrCdr[utils.Subject],
Destination: cgrCdr[utils.Destination],
ExtraFields: cgrCdr.getExtraFields(),
ExtraInfo: cgrCdr[utils.ExtraInfo],
CostSource: cgrCdr[utils.CostSource],
}
if orderID, hasIt := cgrCdr[utils.OrderID]; hasIt {
if storCdr.OrderID, err = strconv.ParseInt(orderID, 10, 64); err != nil {
return nil, err
}
}
storCdr.SetupTime, err = utils.ParseTimeDetectLayout(cgrCdr[utils.SetupTime], timezone) // Not interested to process errors, should do them if necessary in a previous step
if err != nil {
return nil, err
}
storCdr.AnswerTime, err = utils.ParseTimeDetectLayout(cgrCdr[utils.AnswerTime], timezone)
if err != nil {
return nil, err
}
storCdr.Usage, err = utils.ParseDurationWithNanosecs(cgrCdr[utils.Usage])
if err != nil {
return nil, err
}
if partial, hasIt := cgrCdr[utils.Partial]; hasIt {
if storCdr.Partial, err = strconv.ParseBool(partial); err != nil {
return nil, err
}
}
if ratedStr, hasIt := cgrCdr[utils.PreRated]; hasIt {
if storCdr.PreRated, err = strconv.ParseBool(ratedStr); err != nil {
return nil, err
}
}
storCdr.Cost = -1
if costStr, hasIt := cgrCdr[utils.COST]; hasIt {
storCdr.Cost, _ = strconv.ParseFloat(costStr, 64)
if storCdr.Cost, err = strconv.ParseFloat(costStr, 64); err != nil {
return nil, err
}
}
if ratedStr, hasIt := cgrCdr[utils.RATED]; hasIt {
storCdr.PreRated, _ = strconv.ParseBool(ratedStr)
if costDetails, hasIt := cgrCdr[utils.CostDetails]; hasIt {
if err = json.Unmarshal([]byte(costDetails), &storCdr.CostDetails); err != nil {
return nil, err
}
}
return storCdr
return
}

View File

@@ -29,21 +29,31 @@ import (
curl --data "OriginID=asbfdsaf&OriginHost=192.168.1.1&RequestType=rated&tenant=cgrates.org&tor=call&account=1001&subject=1001&destination=1002&time_answer=1383813746&duration=10&field_extr1=val_extr1&fieldextr2=valextr2" http://ipbxdev:2080/cgr
*/
func TestCgrCdrInterfaces(t *testing.T) {
var _ RawCdr = make(CgrCdr)
}
func TestCgrCdrAsCDR(t *testing.T) {
cgrCdr := CgrCdr{utils.ToR: utils.VOICE, utils.OriginID: "dsafdsaf",
utils.OriginHost: "192.168.1.1", utils.Source: "internal_test",
cgrCdr := CgrCdr{
utils.ToR: utils.VOICE,
utils.OriginID: "dsafdsaf",
utils.OriginHost: "192.168.1.1",
utils.Source: "internal_test",
utils.OrderID: "23",
utils.RequestType: utils.META_RATED,
utils.Tenant: "cgrates.org", utils.Category: "call",
utils.Account: "1001", utils.Subject: "1001", utils.Destination: "1002",
utils.SetupTime: "2013-11-07T08:42:20Z", utils.AnswerTime: "2013-11-07T08:42:26Z",
utils.Usage: "10s", "field_extr1": "val_extr1", "fieldextr2": "valextr2"}
utils.Tenant: "cgrates.org",
utils.Category: "call",
utils.Account: "1001",
utils.Subject: "1001",
utils.Destination: "1002",
utils.Partial: "true",
utils.SetupTime: "2013-11-07T08:42:20Z",
utils.AnswerTime: "2013-11-07T08:42:26Z",
utils.Usage: "10s", "field_extr1": "val_extr1", "fieldextr2": "valextr2",
utils.CostDetails: `{ "CGRID": "randomID", "RunID": "thisID"}`,
}
// setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SetupTime], "")
expctRtCdr := &CDR{CGRID: utils.Sha1(cgrCdr[utils.OriginID], cgrCdr[utils.OriginHost]),
ToR: utils.VOICE, OriginID: cgrCdr[utils.OriginID],
expctRtCdr := &CDR{
CGRID: utils.Sha1(cgrCdr[utils.OriginID], cgrCdr[utils.OriginHost]),
ToR: utils.VOICE,
OrderID: 23,
OriginID: cgrCdr[utils.OriginID],
OriginHost: cgrCdr[utils.OriginHost],
Source: cgrCdr[utils.Source],
RequestType: cgrCdr[utils.RequestType],
@@ -52,10 +62,19 @@ func TestCgrCdrAsCDR(t *testing.T) {
Destination: cgrCdr[utils.Destination],
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Usage: 10 * time.Second, Cost: -1,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}
if CDR := cgrCdr.AsCDR(""); !reflect.DeepEqual(expctRtCdr, CDR) {
t.Errorf("Expecting %v, received: %v", expctRtCdr, CDR)
Usage: 10 * time.Second,
Cost: -1,
Partial: true,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
CostDetails: &EventCost{
CGRID: "randomID",
RunID: "thisID",
},
}
if CDR, err := cgrCdr.AsCDR(""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expctRtCdr, CDR) {
t.Errorf("Expecting %v \n, received: %v", utils.ToJSON(expctRtCdr), utils.ToJSON(CDR))
}
}
@@ -73,8 +92,8 @@ func TestReplicatedCgrCdrAsCDR(t *testing.T) {
utils.SetupTime: "2013-11-07T08:42:20Z",
utils.AnswerTime: "2013-11-07T08:42:26Z",
utils.Usage: "10s", utils.COST: "0.12",
utils.RATED: "true", "field_extr1": "val_extr1",
"fieldextr2": "valextr2"}
utils.PreRated: "true",
}
expctRtCdr := &CDR{
CGRID: cgrCdr[utils.CGRID],
ToR: cgrCdr[utils.ToR],
@@ -87,13 +106,96 @@ func TestReplicatedCgrCdrAsCDR(t *testing.T) {
Account: cgrCdr[utils.Account],
Subject: cgrCdr[utils.Subject],
Destination: cgrCdr[utils.Destination],
ExtraFields: map[string]string{},
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Usage: 10 * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
Cost: 0.12, PreRated: true,
Cost: 0.12,
PreRated: true,
}
if CDR := cgrCdr.AsCDR(""); !reflect.DeepEqual(expctRtCdr, CDR) {
if CDR, err := cgrCdr.AsCDR(""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expctRtCdr, CDR) {
t.Errorf("Expecting %v, received: %v", expctRtCdr, CDR)
}
}
func TestCgrCdrAsCDROrderIDError(t *testing.T) {
cgrCdr := CgrCdr{
utils.OrderID: "25.3",
}
expected := "strconv.ParseInt: parsing \"25.3\": invalid syntax"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRSetupTimeError(t *testing.T) {
cgrCdr := CgrCdr{
utils.SetupTime: "invalideTimeFormat",
}
expected := "Unsupported time format"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRAnswerTimeError(t *testing.T) {
cgrCdr := CgrCdr{
utils.AnswerTime: "invalideTimeFormat",
}
expected := "Unsupported time format"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRUsageError(t *testing.T) {
cgrCdr := CgrCdr{
utils.Usage: "1ss",
}
expected := "time: unknown unit \"ss\" in duration \"1ss\""
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRPartialError(t *testing.T) {
cgrCdr := CgrCdr{
utils.Partial: "InvalidBoolFormat",
}
expected := "strconv.ParseBool: parsing \"InvalidBoolFormat\": invalid syntax"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRPreRatedError(t *testing.T) {
cgrCdr := CgrCdr{
utils.PreRated: "InvalidBoolFormat",
}
expected := "strconv.ParseBool: parsing \"InvalidBoolFormat\": invalid syntax"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRCostError(t *testing.T) {
cgrCdr := CgrCdr{
utils.Cost: "InvalidFlaotFormat",
}
expected := "strconv.ParseFloat: parsing \"InvalidFlaotFormat\": invalid syntax"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}
func TestCgrCdrAsCDRCostDetailsError(t *testing.T) {
cgrCdr := CgrCdr{
utils.CostDetails: `{ "CGRID": "randomID", "RunID": 1234}`,
}
expected := "json: cannot unmarshal number into Go struct field EventCost.RunID of type string"
if _, err := cgrCdr.AsCDR(""); err == nil || err.Error() != expected {
t.Errorf("Expecting %v, received: %v", expected, err)
}
}

View File

@@ -392,10 +392,6 @@ var body = []byte(`{
var fsCdrCfg *config.CGRConfig
func TestFsCdrInterfaces(t *testing.T) {
var _ RawCdr = new(FSCdr)
}
func TestFsCdrFirstNonEmpty(t *testing.T) {
fsCdrCfg, _ = config.NewDefaultCGRConfig()
fsCdr, err := NewFSCdr(body, fsCdrCfg)