From c4082547436e5688db2aed85cdc6326d45d9c56e Mon Sep 17 00:00:00 2001 From: porosnicuadrian Date: Mon, 2 Nov 2020 18:02:08 +0200 Subject: [PATCH] Synched Http CDR with CDR object --- engine/cdrs.go | 11 +++- engine/cgrcdr.go | 79 ++++++++++++++++------- engine/cgrcdr_test.go | 144 ++++++++++++++++++++++++++++++++++++------ engine/fscdr_test.go | 4 -- 4 files changed, 187 insertions(+), 51 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index 0d5de052d..8783376e5 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -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( diff --git a/engine/cgrcdr.go b/engine/cgrcdr.go index 84d89943f..a4d42e16e 100644 --- a/engine/cgrcdr.go +++ b/engine/cgrcdr.go @@ -19,13 +19,14 @@ along with this program. If not, see 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 } diff --git a/engine/cgrcdr_test.go b/engine/cgrcdr_test.go index 12159b548..abc6b29f5 100644 --- a/engine/cgrcdr_test.go +++ b/engine/cgrcdr_test.go @@ -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) + } +} diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go index c8e343b30..73fe1b597 100644 --- a/engine/fscdr_test.go +++ b/engine/fscdr_test.go @@ -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)