diff --git a/cdrs/fscdr.go b/cdrs/fscdr.go index cac1be61e..c377326d7 100644 --- a/cdrs/fscdr.go +++ b/cdrs/fscdr.go @@ -22,10 +22,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/cgrates/cgrates/utils" + "reflect" "strconv" "strings" "time" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" ) const ( @@ -51,17 +54,19 @@ const ( FS_SIP_REQUSER = "sip_req_user" // Apps like FusionPBX do not set dialed_extension, alternative being destination_number but that comes in customer profile, not in vars ) -type FSCdr map[string]string +type FSCdr struct { + vars map[string]string + body map[string]interface{} +} func (fsCdr FSCdr) New(body []byte) (utils.RawCDR, error) { - fsCdr = make(map[string]string) - var tmp map[string]interface{} + fsCdr.vars = make(map[string]string) var err error - if err = json.Unmarshal(body, &tmp); err == nil { - if variables, ok := tmp[FS_CDR_MAP]; ok { + if err = json.Unmarshal(body, &fsCdr.body); err == nil { + if variables, ok := fsCdr.body[FS_CDR_MAP]; ok { if variables, ok := variables.(map[string]interface{}); ok { for k, v := range variables { - fsCdr[k] = v.(string) + fsCdr.vars[k] = v.(string) } } return fsCdr, nil @@ -71,13 +76,13 @@ func (fsCdr FSCdr) New(body []byte) (utils.RawCDR, error) { } func (fsCdr FSCdr) GetCgrId() string { - return utils.FSCgrId(fsCdr[FS_UUID]) + return utils.FSCgrId(fsCdr.vars[FS_UUID]) } func (fsCdr FSCdr) GetAccId() string { - return fsCdr[FS_UUID] + return fsCdr.vars[FS_UUID] } func (fsCdr FSCdr) GetCdrHost() string { - return fsCdr[FS_IP] + return fsCdr.vars[FS_IP] } func (fsCdr FSCdr) GetCdrSource() string { return FS_CDR_SOURCE @@ -87,55 +92,88 @@ func (fsCdr FSCdr) GetDirection() string { return "*out" } func (fsCdr FSCdr) GetSubject() string { - return utils.FirstNonEmpty(fsCdr[FS_SUBJECT], fsCdr[FS_USERNAME]) + return utils.FirstNonEmpty(fsCdr.vars[FS_SUBJECT], fsCdr.vars[FS_USERNAME]) } func (fsCdr FSCdr) GetAccount() string { - return utils.FirstNonEmpty(fsCdr[FS_ACCOUNT], fsCdr[FS_USERNAME]) + return utils.FirstNonEmpty(fsCdr.vars[FS_ACCOUNT], fsCdr.vars[FS_USERNAME]) } // Charging destination number func (fsCdr FSCdr) GetDestination() string { - return utils.FirstNonEmpty(fsCdr[FS_DESTINATION], fsCdr[FS_CALL_DEST_NR], fsCdr[FS_SIP_REQUSER]) + return utils.FirstNonEmpty(fsCdr.vars[FS_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER]) } func (fsCdr FSCdr) GetTOR() string { - return utils.FirstNonEmpty(fsCdr[FS_TOR], cfg.DefaultTOR) + return utils.FirstNonEmpty(fsCdr.vars[FS_TOR], cfg.DefaultTOR) } func (fsCdr FSCdr) GetTenant() string { - return utils.FirstNonEmpty(fsCdr[FS_CSTMID], cfg.DefaultTenant) + return utils.FirstNonEmpty(fsCdr.vars[FS_CSTMID], cfg.DefaultTenant) } func (fsCdr FSCdr) GetReqType() string { - return utils.FirstNonEmpty(fsCdr[FS_REQTYPE], cfg.DefaultReqType) + return utils.FirstNonEmpty(fsCdr.vars[FS_REQTYPE], cfg.DefaultReqType) } func (fsCdr FSCdr) GetExtraFields() map[string]string { extraFields := make(map[string]string, len(cfg.CDRSExtraFields)) for _, field := range cfg.CDRSExtraFields { - extraFields[field] = fsCdr[field] + if extraValue, found := fsCdr.vars[field]; found { + extraFields[field] = extraValue + } else { + extraFields[field] = fsCdr.searchExtraField(field, fsCdr.body) + } } return extraFields } + +func (fsCdr FSCdr) searchExtraField(field string, body map[string]interface{}) (result string) { + for key, value := range body { + switch v := value.(type) { + case string: + if key == field { + return v + } + case map[string]interface{}: + if result = fsCdr.searchExtraField(field, v); result != "" { + return + } + case []interface{}: + for _, item := range v { + if otherMap, ok := item.(map[string]interface{}); ok { + if result = fsCdr.searchExtraField(field, otherMap); result != "" { + return + } + } else { + engine.Logger.Warning(fmt.Sprintf("Slice with no maps: %v", reflect.TypeOf(item))) + } + } + default: + engine.Logger.Warning(fmt.Sprintf("Unexpected type: %v", reflect.TypeOf(v))) + } + } + return +} + func (fsCdr FSCdr) GetSetupTime() (t time.Time, err error) { //ToDo: Make sure we work with UTC instead of local time - at, err := strconv.ParseInt(fsCdr[FS_SETUP_TIME], 0, 64) + at, err := strconv.ParseInt(fsCdr.vars[FS_SETUP_TIME], 0, 64) t = time.Unix(at, 0) return } func (fsCdr FSCdr) GetAnswerTime() (t time.Time, err error) { //ToDo: Make sure we work with UTC instead of local time - at, err := strconv.ParseInt(fsCdr[FS_ANSWER_TIME], 0, 64) + at, err := strconv.ParseInt(fsCdr.vars[FS_ANSWER_TIME], 0, 64) t = time.Unix(at, 0) return } func (fsCdr FSCdr) GetHangupTime() (t time.Time, err error) { - hupt, err := strconv.ParseInt(fsCdr[FS_HANGUP_TIME], 0, 64) + hupt, err := strconv.ParseInt(fsCdr.vars[FS_HANGUP_TIME], 0, 64) t = time.Unix(hupt, 0) return } // Extracts duration as considered by the telecom switch func (fsCdr FSCdr) GetDuration() time.Duration { - dur, _ := utils.ParseDurationWithSecs(fsCdr[FS_DURATION]) + dur, _ := utils.ParseDurationWithSecs(fsCdr.vars[FS_DURATION]) return dur } @@ -197,40 +235,40 @@ func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFl } if strings.HasPrefix(reqTypeFld, utils.STATIC_VALUE_PREFIX) { // Values starting with prefix are not dynamically populated rtCdr.ReqType = reqTypeFld[1:] - } else if rtCdr.ReqType, hasKey = fsCdr[reqTypeFld]; !hasKey && fieldsMandatory { + } else if rtCdr.ReqType, hasKey = fsCdr.vars[reqTypeFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, reqTypeFld)) } if strings.HasPrefix(directionFld, utils.STATIC_VALUE_PREFIX) { rtCdr.Direction = directionFld[1:] - } else if rtCdr.Direction, hasKey = fsCdr[directionFld]; !hasKey && fieldsMandatory { + } else if rtCdr.Direction, hasKey = fsCdr.vars[directionFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, directionFld)) } if strings.HasPrefix(tenantFld, utils.STATIC_VALUE_PREFIX) { rtCdr.Tenant = tenantFld[1:] - } else if rtCdr.Tenant, hasKey = fsCdr[tenantFld]; !hasKey && fieldsMandatory { + } else if rtCdr.Tenant, hasKey = fsCdr.vars[tenantFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, tenantFld)) } if strings.HasPrefix(torFld, utils.STATIC_VALUE_PREFIX) { rtCdr.TOR = torFld[1:] - } else if rtCdr.TOR, hasKey = fsCdr[torFld]; !hasKey && fieldsMandatory { + } else if rtCdr.TOR, hasKey = fsCdr.vars[torFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, torFld)) } if strings.HasPrefix(accountFld, utils.STATIC_VALUE_PREFIX) { rtCdr.Account = accountFld[1:] - } else if rtCdr.Account, hasKey = fsCdr[accountFld]; !hasKey && fieldsMandatory { + } else if rtCdr.Account, hasKey = fsCdr.vars[accountFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, accountFld)) } if strings.HasPrefix(subjectFld, utils.STATIC_VALUE_PREFIX) { rtCdr.Subject = subjectFld[1:] - } else if rtCdr.Subject, hasKey = fsCdr[subjectFld]; !hasKey && fieldsMandatory { + } else if rtCdr.Subject, hasKey = fsCdr.vars[subjectFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, subjectFld)) } if strings.HasPrefix(destFld, utils.STATIC_VALUE_PREFIX) { rtCdr.Destination = destFld[1:] - } else if rtCdr.Destination, hasKey = fsCdr[destFld]; !hasKey && fieldsMandatory { + } else if rtCdr.Destination, hasKey = fsCdr.vars[destFld]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, destFld)) } - if sTimeStr, hasKey = fsCdr[setupTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(setupTimeFld, utils.STATIC_VALUE_PREFIX) { + if sTimeStr, hasKey = fsCdr.vars[setupTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(setupTimeFld, utils.STATIC_VALUE_PREFIX) { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, setupTimeFld)) } else { if strings.HasPrefix(setupTimeFld, utils.STATIC_VALUE_PREFIX) { @@ -240,7 +278,7 @@ func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFl return nil, err } } - if aTimeStr, hasKey = fsCdr[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, utils.STATIC_VALUE_PREFIX) { + if aTimeStr, hasKey = fsCdr.vars[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, utils.STATIC_VALUE_PREFIX) { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, answerTimeFld)) } else { if strings.HasPrefix(answerTimeFld, utils.STATIC_VALUE_PREFIX) { @@ -250,7 +288,7 @@ func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFl return nil, err } } - if durStr, hasKey = fsCdr[durationFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(durationFld, utils.STATIC_VALUE_PREFIX) { + if durStr, hasKey = fsCdr.vars[durationFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(durationFld, utils.STATIC_VALUE_PREFIX) { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, durationFld)) } else { if strings.HasPrefix(durationFld, utils.STATIC_VALUE_PREFIX) { @@ -262,7 +300,7 @@ func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFl } rtCdr.ExtraFields = make(map[string]string, len(extraFlds)) for _, fldName := range extraFlds { - if fldVal, hasKey := fsCdr[fldName]; !hasKey && fieldsMandatory { + if fldVal, hasKey := fsCdr.vars[fldName]; !hasKey && fieldsMandatory { return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, fldName)) } else { rtCdr.ExtraFields[fldName] = fldVal diff --git a/cdrs/fscdr_test.go b/cdrs/fscdr_test.go index 605a5f2ec..da2f6ba2d 100644 --- a/cdrs/fscdr_test.go +++ b/cdrs/fscdr_test.go @@ -19,11 +19,12 @@ along with this program. If not, see package cdrs import ( - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/utils" "reflect" "testing" "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" ) var body = []byte(`{"core-uuid":"844715f9-d8a1-44d6-a4bf-358bec5e10b8","channel_data":{"state":"CS_REPORTING","direction":"inbound","state_number":"11","flags":"0=1;1=1;3=1;19=1;23=1;36=1;37=1;39=1;42=1;47=1;52=1","caps":"1=1;2=1;3=1;4=1;5=1;6=1"},"variables":{"direction":"inbound","uuid":"01df56f4-d99a-4ef6-b7fe-b924b2415b7f","session_id":"33","sip_from_user":"dan","sip_from_uri":"dan@ipbx.itsyscom.com","sip_from_host":"ipbx.itsyscom.com","channel_name":"sofia/ipbxas/dan@ipbx.itsyscom.com","sip_local_network_addr":"127.0.0.1","sip_network_ip":"2.3.4.5","sip_network_port":"5060","sip_received_ip":"2.3.4.5","sip_received_port":"5060","sip_via_protocol":"udp","sip_from_user_stripped":"dan","sofia_profile_name":"ipbxas","recovery_profile_name":"ipbxas","sip_invite_record_route":"","sip_req_user":"+4986517174963","sip_req_port":"5080","sip_req_uri":"+4986517174963@127.0.0.1:5080","sip_req_host":"127.0.0.1","sip_to_user":"+4986517174963","sip_to_uri":"+4986517174963@ipbx.itsyscom.com","sip_to_host":"ipbx.itsyscom.com","sip_contact_params":"alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com","sip_contact_user":"dan","sip_contact_port":"5060","sip_contact_uri":"dan@10.10.10.154:5060","sip_contact_host":"10.10.10.154","sip_user_agent":"Jitsi2.2.4603.9615Linux","sip_via_host":"2.3.4.5","presence_id":"dan@ipbx.itsyscom.com","sip_h_X-AuthType":"SUA","sip_h_X-AuthUser":"dan","sip_h_X-AuthDomain":"ipbx.itsyscom.com","sip_h_X-BalancerIP":"2.3.4.5","switch_r_sdp":"v=0\r\no=dan 0 0 IN IP4 10.10.10.154\r\ns=-\r\nc=IN IP4 10.10.10.154\r\nt=0 0\r\nm=audio 5004 RTP/AVP 96 8 0\r\na=rtpmap:96 opus/48000\r\na=fmtp:96 usedtx=1\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=extmap:1 urn:ietf:params:rtp-hdrext:csrc-audio-level\r\nm=video 5006 RTP/AVP 97 99\r\na=rtpmap:97 H264/90000\r\na=fmtp:97 profile-level-id=4DE01f;packetization-mode=1\r\na=rtpmap:99 H264/90000\r\na=fmtp:99 profile-level-id=4DE01f\r\na=recvonly\r\na=imageattr:97 send [x=[0-640],y=[0-480]] recv [x=[0-1280],y=[0-800]]\r\na=imageattr:99 send [x=[0-640],y=[0-480]] recv [x=[0-1280],y=[0-800]]\r\n","ep_codec_string":"PCMA@8000h@20i@64000b,PCMU@8000h@20i@64000b,H264@90000h","effective_caller_id_number":"+4986517174960","hangup_after_bridge":"true","continue_on_fail":"true","cgr_tenant":"ipbx.itsyscom.com","cgr_tor":"call","cgr_account":"dan","cgr_subject":"dan","cgr_destination":"+4986517174963","sip_redirect_contact_0":";q=1","sip_redirected_to":";q=1","sip_redirect_contact_user_0":"dan","sip_redirect_contact_host_0":"10.10.10.141","sip_redirect_contact_params_0":"alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072","sip_redirect_dialstring_0":"sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072","sip_redirect_contact_1":"","sip_redirect_contact_user_1":"dan","sip_redirect_contact_host_1":"10.10.10.154","sip_redirect_contact_params_1":"alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060","sip_redirect_dialstring_1":"sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060","sip_redirect_dialstring":"sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072,sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060","max_forwards":"15","transfer_history":"1375609854:d2300128-6724-471c-a495-a3f7a985a2b6:bl_xfer:dan/redirected/XML","transfer_source":"1375609854:d2300128-6724-471c-a495-a3f7a985a2b6:bl_xfer:dan/redirected/XML","call_uuid":"01df56f4-d99a-4ef6-b7fe-b924b2415b7f","current_application_data":"sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060;fs_path=sip:2.3.4.5,sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072;fs_path=sip:2.3.4.5","current_application":"bridge","originated_legs":"ARRAY::e377c077-0f1f-4b7d-b036-1aeef13eff32;Outbound Call;dan|:8f7c860f-0619-4d3c-9515-cc23b0fa3997;Outbound Call;dan","switch_m_sdp":"v=0\r\no=root 975388641 975388642 IN IP4 10.10.10.141\r\ns=call\r\nc=IN IP4 10.10.10.141\r\nt=0 0\r\nm=audio 59976 RTP/AVP 8 0 9 3 101\r\na=rtpmap:8 pcma/8000\r\na=rtpmap:0 pcmu/8000\r\na=rtpmap:9 g722/8000\r\na=rtpmap:3 gsm/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=ptime:20\r\nm=video 0 RTP/AVP 98 99\r\na=rtpmap:98 H264/90000\r\na=fmtp:98 profile-level-id=4DE01f\r\na=rtpmap:99 H264/90000\r\na=fmtp:99 profile-level-id=4DE01f\r\n","rtp_use_codec_string":"G722,G722.1,G729,PCMU,PCMA,GSM,H264","sip_audio_recv_pt":"0","sip_use_codec_name":"PCMU","sip_use_codec_rate":"8000","sip_use_codec_ptime":"20","read_codec":"PCMU","read_rate":"8000","write_codec":"PCMU","write_rate":"8000","dtmf_type":"info","video_possible":"true","remote_video_ip":"10.10.10.154","remote_video_port":"5006","sip_video_fmtp":"profile-level-id=4DE01f;packetization-mode=1","sip_video_pt":"97","sip_video_recv_pt":"97","video_read_codec":"H264","video_read_rate":"90000","video_write_codec":"H264","video_write_rate":"90000","sip_use_video_codec_name":"H264","sip_use_video_codec_fmtp":"profile-level-id=4DE01f;packetization-mode=1","sip_use_video_codec_rate":"90000","sip_use_video_codec_ptime":"0","local_media_ip":"2.3.4.5","local_media_port":"29452","advertised_media_ip":"2.3.4.5","sip_use_pt":"0","rtp_use_ssrc":"1408273224","local_video_ip":"2.3.4.5","local_video_port":"22648","sip_use_video_pt":"97","rtp_use_video_ssrc":"1408273224","sip_local_sdp_str":"v=0\no=iPBXCell 1375580404 1375580405 IN IP4 2.3.4.5\ns=iPBXCell\nc=IN IP4 2.3.4.5\nt=0 0\nm=audio 29452 RTP/AVP 0\na=rtpmap:0 PCMU/8000\na=silenceSupp:off - - - -\na=ptime:20\na=sendrecv\nm=video 22648 RTP/AVP 97\na=rtpmap:97 H264/90000\n","endpoint_disposition":"ANSWER","originate_disposition":"SUCCESS","DIALSTATUS":"SUCCESS","originate_causes":"ARRAY::e377c077-0f1f-4b7d-b036-1aeef13eff32;LOSE_RACE|:8f7c860f-0619-4d3c-9515-cc23b0fa3997;NONE","last_bridge_to":"8f7c860f-0619-4d3c-9515-cc23b0fa3997","bridge_channel":"sofia/ipbxas/sip:dan@10.10.10.141:3072","bridge_uuid":"8f7c860f-0619-4d3c-9515-cc23b0fa3997","signal_bond":"8f7c860f-0619-4d3c-9515-cc23b0fa3997","sip_to_tag":"4X345vQvQyetD","sip_from_tag":"9f90cc40","sip_cseq":"2","sip_call_id":"ca9c5e20caeaa6596be8cf66261f13e5@0:0:0:0:0:0:0:0","sip_full_via":"SIP/2.0/UDP 2.3.4.5;branch=z9hG4bKcydzigwkX,SIP/2.0/UDP 10.10.10.154:5060;rport=5060;received=1.2.3.4;branch=z9hG4bK-313937-b78ce2a1daafe532fc34b1b3735727ac","sip_from_display":"dan","sip_full_from":"\"dan\" ;tag=9f90cc40","sip_full_to":";tag=4X345vQvQyetD","last_sent_callee_id_name":"Outbound Call","last_sent_callee_id_number":"dan","remote_media_ip_reported":"10.10.10.154","remote_media_ip":"1.2.3.4","remote_media_port_reported":"5004","remote_media_port":"5004","rtp_auto_adjust":"true","sip_hangup_phrase":"OK","last_bridge_hangup_cause":"NORMAL_CLEARING","last_bridge_proto_specific_hangup_cause":"sip:200","bridge_hangup_cause":"NORMAL_CLEARING","hangup_cause":"NORMAL_CLEARING","hangup_cause_q850":"16","digits_dialed":"none","start_stamp":"2013-08-04 11:50:54","profile_start_stamp":"2013-08-04 11:50:54","answer_stamp":"2013-08-04 11:50:56","bridge_stamp":"2013-08-04 11:50:56","progress_stamp":"2013-08-04 11:50:54","progress_media_stamp":"2013-08-04 11:50:56","end_stamp":"2013-08-04 11:51:00","start_epoch":"1375609854","start_uepoch":"1375609854385581","profile_start_epoch":"1375609854","profile_start_uepoch":"1375609854385581","answer_epoch":"1375609856","answer_uepoch":"1375609856285587","bridge_epoch":"1375609856","bridge_uepoch":"1375609856285587","last_hold_epoch":"0","last_hold_uepoch":"0","hold_accum_seconds":"0","hold_accum_usec":"0","hold_accum_ms":"0","resurrect_epoch":"0","resurrect_uepoch":"0","progress_epoch":"1375609854","progress_uepoch":"1375609854505584","progress_media_epoch":"1375609856","progress_media_uepoch":"1375609856285587","end_epoch":"1375609860","end_uepoch":"1375609860205563","last_app":"bridge","last_arg":"sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060;fs_path=sip:2.3.4.5,sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072;fs_path=sip:2.3.4.5","caller_id":"\"dan\" ","duration":"6","billsec":"4","progresssec":"0","answersec":"2","waitsec":"2","progress_mediasec":"2","flow_billsec":"6","mduration":"5820","billmsec":"3920","progressmsec":"120","answermsec":"1900","waitmsec":"1900","progress_mediamsec":"1900","flow_billmsec":"5820","uduration":"5819982","billusec":"3919976","progressusec":"120003","answerusec":"1900006","waitusec":"1900006","progress_mediausec":"1900006","flow_billusec":"5819982","sip_hangup_disposition":"send_bye","rtp_audio_in_raw_bytes":"32968","rtp_audio_in_media_bytes":"32960","rtp_audio_in_packet_count":"207","rtp_audio_in_media_packet_count":"205","rtp_audio_in_skip_packet_count":"6","rtp_audio_in_jb_packet_count":"0","rtp_audio_in_dtmf_packet_count":"0","rtp_audio_in_cng_packet_count":"0","rtp_audio_in_flush_packet_count":"2","rtp_audio_in_largest_jb_size":"0","rtp_audio_out_raw_bytes":"31648","rtp_audio_out_media_bytes":"31648","rtp_audio_out_packet_count":"184","rtp_audio_out_media_packet_count":"184","rtp_audio_out_skip_packet_count":"0","rtp_audio_out_dtmf_packet_count":"0","rtp_audio_out_cng_packet_count":"0","rtp_audio_rtcp_packet_count":"0","rtp_audio_rtcp_octet_count":"0","rtp_video_in_raw_bytes":"0","rtp_video_in_media_bytes":"0","rtp_video_in_packet_count":"0","rtp_video_in_media_packet_count":"0","rtp_video_in_skip_packet_count":"0","rtp_video_in_jb_packet_count":"0","rtp_video_in_dtmf_packet_count":"0","rtp_video_in_cng_packet_count":"0","rtp_video_in_flush_packet_count":"0","rtp_video_in_largest_jb_size":"0","rtp_video_out_raw_bytes":"0","rtp_video_out_media_bytes":"0","rtp_video_out_packet_count":"0","rtp_video_out_media_packet_count":"0","rtp_video_out_skip_packet_count":"0","rtp_video_out_dtmf_packet_count":"0","rtp_video_out_cng_packet_count":"0","rtp_video_rtcp_packet_count":"0","rtp_video_rtcp_octet_count":"0"},"app_log":{"applications":[{"app_name":"set","app_data":"effective_caller_id_number=+4986517174960"},{"app_name":"set","app_data":"hangup_after_bridge=true"},{"app_name":"set","app_data":"continue_on_fail=true"},{"app_name":"set","app_data":"cgr_tenant=ipbx.itsyscom.com"},{"app_name":"set","app_data":"cgr_tor=call"},{"app_name":"set","app_data":"cgr_account=dan"},{"app_name":"set","app_data":"cgr_subject=dan"},{"app_name":"set","app_data":"cgr_destination=+4986517174963"},{"app_name":"bridge","app_data":"{presence_id=dan@ipbx.itsyscom.com,sip_redirect_fork=true}sofia/ipbxas/dan@ipbx.itsyscom.com;fs_path=sip:2.3.4.5"},{"app_name":"bridge","app_data":"sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060;fs_path=sip:2.3.4.5,sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072;fs_path=sip:2.3.4.5"}]},"callflow":{"dialplan":"XML","profile_index":"2","extension":{"name":"Redirected call","number":"dan","applications":[{"app_name":"bridge","app_data":"sofia/ipbxas/sip:dan@10.10.10.154:5060;alias=1.2.3.4~5060~1;transport=udp;registering_acc=ipbx_itsyscom_com;rcv=sip:1.2.3.4:5060;fs_path=sip:2.3.4.5,sofia/ipbxas/sip:dan@10.10.10.141:3072;alias=1.2.3.4~3072~1;line=x81npwse;rcv=sip:1.2.3.4:3072;fs_path=sip:2.3.4.5"}]},"caller_profile":{"username":"dan","dialplan":"XML","caller_id_name":"dan","ani":"dan","aniii":"","caller_id_number":"dan","network_addr":"2.3.4.5","rdnis":"+4986517174963","destination_number":"dan","uuid":"01df56f4-d99a-4ef6-b7fe-b924b2415b7f","source":"mod_sofia","context":"redirected","chan_name":"sofia/ipbxas/dan@ipbx.itsyscom.com","originatee":{"originatee_caller_profiles":[{"username":"dan","dialplan":"XML","caller_id_name":"dan","ani":"dan","aniii":"","caller_id_number":"+4986517174960","network_addr":"2.3.4.5","rdnis":"+4986517174963","destination_number":"dan","uuid":"8f7c860f-0619-4d3c-9515-cc23b0fa3997","source":"mod_sofia","context":"redirected","chan_name":"sofia/ipbxas/sip:dan@10.10.10.141:3072"}]}},"times":{"created_time":"1375609854385581","profile_created_time":"1375609854385581","progress_time":"1375609854505584","progress_media_time":"1375609856285587","answered_time":"1375609856285587","hangup_time":"1375609860205563","resurrect_time":"0","transfer_time":"0"}},"callflow":{"dialplan":"XML","profile_index":"1","extension":{"name":"OnNet Call","number":"+4986517174963","applications":[{"app_name":"set","app_data":"effective_caller_id_number=+4986517174960"},{"app_name":"set","app_data":"hangup_after_bridge=true"},{"app_name":"set","app_data":"continue_on_fail=true"},{"app_name":"set","app_data":"cgr_tenant=ipbx.itsyscom.com"},{"app_name":"set","app_data":"cgr_tor=call"},{"app_name":"set","app_data":"cgr_account=dan"},{"app_name":"set","app_data":"cgr_subject=dan"},{"app_name":"set","app_data":"cgr_destination=+4986517174963"},{"app_name":"bridge","app_data":"{presence_id=dan@ipbx.itsyscom.com,sip_redirect_fork=true}sofia/ipbxas/dan@ipbx.itsyscom.com;fs_path=sip:2.3.4.5"}]},"caller_profile":{"username":"dan","dialplan":"XML","caller_id_name":"dan","ani":"dan","aniii":"","caller_id_number":"dan","network_addr":"2.3.4.5","rdnis":"","destination_number":"+4986517174963","uuid":"01df56f4-d99a-4ef6-b7fe-b924b2415b7f","source":"mod_sofia","context":"ipbxas","chan_name":"sofia/ipbxas/dan@ipbx.itsyscom.com"},"times":{"created_time":"1375609854385581","profile_created_time":"1375609854385581","progress_time":"0","progress_media_time":"0","answered_time":"0","hangup_time":"0","resurrect_time":"0","transfer_time":"1375609854385581"}}}`) @@ -34,7 +35,7 @@ func TestFirstNonEmpty(t *testing.T) { t.Errorf("Error loading cdr: %v", err) } fsc := fsCdr.(FSCdr) - if _, ok := fsc["cgr_destination"]; !ok { + if _, ok := fsc.vars["cgr_destination"]; !ok { t.Error("Error parsing cdr: ", fsCdr) } } @@ -113,7 +114,7 @@ func TestFsCdrAsStoredCdr(t *testing.T) { expctRatedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1", CdrSource: FS_CDR_SOURCE, ReqType: utils.RATED, Direction: "*out", Tenant: "ipbx.itsyscom.com", TOR: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963", - SetupTime: time.Date(2013, 8, 4, 9, 50, 54, 0, time.UTC).Local(), + SetupTime: time.Date(2013, 8, 4, 9, 50, 54, 0, time.UTC).Local(), AnswerTime: time.Date(2013, 8, 4, 9, 50, 56, 0, time.UTC).Local(), Duration: time.Duration(4) * time.Second, ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1} if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) { @@ -127,7 +128,7 @@ func TestFsCdrAsStoredCdr(t *testing.T) { expctRatedCdr2 := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1", CdrSource: FS_CDR_SOURCE, ReqType: "postpaid", Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "+4986517174963", - SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), + SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second, ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1} if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { @@ -138,3 +139,28 @@ func TestFsCdrAsStoredCdr(t *testing.T) { t.Error("Failed to detect missing header") } } + +func TestSearchExtraFieldLast(t *testing.T) { + fsCdr, _ := new(FSCdr).New(body) + value := fsCdr.(FSCdr).searchExtraField("transfer_time", fsCdr.(FSCdr).body) + if value != "1375609854385581" { + t.Error("Error finding extra field: ", value) + } +} + +func TestSearchExtraField(t *testing.T) { + fsCdr, _ := new(FSCdr).New(body) + cfg.CDRSExtraFields = []string{"caller_id_name"} + extraFields := fsCdr.GetExtraFields() + if len(extraFields) != 1 || extraFields["caller_id_name"] != "dan" { + t.Error("Error parsing extra fields: ", extraFields) + } +} + +func TestSearchExtraFieldInSlice(t *testing.T) { + fsCdr, _ := new(FSCdr).New(body) + value := fsCdr.(FSCdr).searchExtraField("app_data", fsCdr.(FSCdr).body) + if value != "effective_caller_id_number=+4986517174960" { + t.Error("Error finding extra field: ", value) + } +}