System.NullReferenceException: Object reference not set to an instance of an object.

Status
Not open for further replies.

drenda

Forum User
Joined
May 3, 2020
Messages
30
Reaction score
10
Hi,
I created this CRM template.


XML:
<?xml version="1.0"?>
<Crm Country="IT" Name="TestApp" Version="1">
    <Number Prefix="AsIs" MaxLength="[MaxLength]"/>
    <Connection MaxConcurrentRequests="10"/>
    <Parameters>
        <Parameter Name="ReportCallEnabled" Type="Boolean" Editor="String" Title="Enable Call Journaling" Default="True"/>
        <Parameter Name="ApiKey" Type="String" Parent="General Configuration" Editor="String" Title="ApiKey" Default="{Token}"/>
        <Parameter Name="TestAppClient" Type="String" Parent="General Configuration" Editor="String" Title="TestApp client URL" Default="{TestAppClient}"/>
        <Parameter Name="TestAppServer" Type="String" Parent="General Configuration" Editor="String" Title="TestApp server url" Default="{TestAppServer}"/>
    </Parameters>
    <Authentication Type="No"/>
    <Scenarios>
        <Scenario Id="" Type="REST">
            <Request Url="[TestAppServer]/api/v1/integrations/3cx/contacts?phone-number=[Number]" MessagePasses="0" RequestEncoding="UrlEncoded"
                     RequestType="Get" ResponseType="Json">
                <Headers>
                    <Value Key="Authorization" Passes="0" Type="String">Bearer [APIkey]</Value>
                </Headers>
            </Request>
            <Rules>
                <Rule Type="Any">contact.id</Rule>
            </Rules>
            <Variables>
                <Variable Name="ContactID" Path="contact.id">
                    <Filter/>
                </Variable>
                <Variable Name="FirstName" Path="contact.firstName">
                    <Filter/>
                </Variable>
                <Variable Name="LastName" Path="contact.lastName">
                    <Filter/>
                </Variable>
                <Variable Name="CompanyName" Path="contact.companyName">
                    <Filter/>
                </Variable>
                <Variable Name="Email" Path="contact.email">
                    <Filter/>
                </Variable>
                <Variable Name="PhoneBusiness" Path="contact.landlinePhone">
                    <Filter/>
                </Variable>
                <Variable Name="PhoneMobile" Path="contact.mobilePhone">
                    <Filter/>
                </Variable>
            </Variables>
            <Outputs AllowEmpty="false">
                <Output Type="EntityType" Passes="0" Value="Contact"/>
                <Output Type="EntityId" Passes="0" Value="[ContactID]"/>
                <Output Type="FirstName" Passes="0" Value="[FirstName]"/>
                <Output Type="LastName" Passes="0" Value="[LastName]"/>
                <Output Type="CompanyName" Passes="0" Value="[CompanyName]"/>
                <Output Type="Email" Passes="0" Value="[Email]"/>
                <Output Type="PhoneBusiness" Passes="0" Value="[PhoneBusiness]"/>
                <Output Type="PhoneMobile" Passes="0" Value="[PhoneMobile]"/>
                <Output Type="ContactUrl" Passes="0" Value="[TestAppClient]/contacts/[ContactID]/dashboard"/>
            </Outputs>
        </Scenario>
        <Scenario Id="ReportCall" Type="REST">
            <Request SkipIf="[ReportCallEnabled]!=True||[EntityId]==&quot;&quot;" Url="[TestAppServer]/api/v1/integrations/3cx/cdr" MessagePasses="0"
                     RequestContentType="application/json" RequestEncoding="Json" RequestType="Post" ResponseType="Json">
                <Headers>
                    <Value Key="Authorization" Passes="0" Type="String">Bearer [APIkey]</Value>
                </Headers>
                <PostValues Key="">
                    <Value Key="number" Passes="1" Type="String">[Number]</Value>
                    <Value Key="callDirection" Passes="1" Type="String">[CallDirection]</Value>
                    <Value Key="callType" Passes="1" Type="String">[CallType]</Value>
                    <Value Key="name" Passes="1" Type="String">[Name]</Value>
                    <Value Key="queueExtension" Passes="1" Type="String">[QueueExtension]</Value>
                    <Value Key="agent" Passes="1" Type="String">[Agent]</Value>
                    <Value Key="agentFirstName" Passes="1" Type="String">[AgentFirstName]</Value>
                    <Value Key="agentLastName" Passes="1" Type="String">[AgentLastName]</Value>
                    <Value Key="agentEmail" Passes="1" Type="String">[AgentEmail]</Value>
                    <Value Key="startDate" Passes="1" Type="String">[[CallStartTimeUTC].ToString("yyyy-MM-dd")]T[[CallStartTimeUTC].ToString("HH:mm:ss")]Z</Value>
                    <Value Key="endDate" Passes="1" Type="String">[[CallEndTimeUTC].ToString("yyyy-MM-dd")]T[[CallEndTimeUTC].ToString("HH:mm:ss")]Z</Value>
                    <Value Key="contactId" Passes="1" Type="String">[EntityId]</Value>
                </PostValues>
            </Request>
            <Outputs AllowEmpty="true"/>
        </Scenario>
    </Scenarios>
</Crm>

This template it's supposed to lookup for a number in the external app and save the CDR.
Unfortunately the CDR is not saved, checking 3CX logs I see this error:

Code:
2022/11/11 09:09:28.645|3120|0034|Erro|CRM: Exception during call reporting: System.NullReferenceException: Object reference not set to an instance of an object.
   at CrmIntegration.ResultVariableScenarioProcessor.ProcessFinalResult(ProtocolScenario scenario, IValueManager valueManager)
   at CrmIntegration.ScenarioProcessorBase.ProcessMatching(ProtocolScenario scenario, MatchingPath matching, ResponseMatcher responseMatcher, IValueManager customManager, HashSet`1 distinctMatchings, CancellationToken token)
   at CrmIntegration.ScenarioProcessorBase.ProcessFoundMatchings(ProtocolScenario scenario, IValueManager customManager, List`1 matchings, ResponseMatcher responseMatcher, CancellationToken token)
   at CrmIntegration.ScenarioProcessorBase.ProcessScenario(ProtocolScenario scenario, IValueManager customManager, CancellationToken token)
   at CrmIntegration.ScenarioProcessorBase.Execute(ProtocolScenario scenario, CancellationToken token)
   at CrmIntegration.CrmProcessor.ExecuteScenarioByName(String scenarioName, IEnumerable`1 parameters, Boolean useNoAuthProvider, CancellationToken token)
   at CrmIntegration.CrmProcessor.ExecuteScenarioWithRetries(String scenarioId, Dictionary`2 parameters, CancellationToken token)
   at CrmIntegration.CrmProcessor.ReportCall(CallEndedEvent callEndedEvent, CancellationToken token)

Can you explain the sense of this message? Is this error depending on something wrong in the template?

Thanks
 
Hello @drenda,

Looks like an issue while processing the response for the call journaling request. Can you post the JSON response returned by the server in this case? You will see this in the 3cxSystemService.log file when making the call while verbose logs are enabled.

Also, in the ReportCall scenario, try changing:
<Outputs AllowEmpty="true"/>

to:
<Outputs AllowEmpty="false"/>

Let me know if that makes a difference.
 
  • Like
Reactions: Evolute IT
Hello @drenda,
Looks like an issue while processing the response for the call journaling request. Can you post the JSON response returned by the server in this case? You will see this in the 3cxSystemService.log file when making the call while verbose logs are enabled.

Also, in the ReportCall scenario, try changing:
<Outputs AllowEmpty="true"/>

to:
<Outputs AllowEmpty="false"/>

Let me know if that makes a difference.
Thanks for your reply. I did try with allowEmpty=false in the ReportCall scenario. I enabled verbose logs and I found something interesting:

Code:
022/11/11 12:51:39.722|22846|0046|Info|CRM: about to report 1 calls at maxConcurrency 1 <-------
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Triggering Call Journaling with values - CallType='Inbound' - PhoneNumber='347xxxxxxxxxxx' - ContactName='' - AgentExtension='002' - AgentFirstName='Test' - AgentLastName='Daniele' - AgentEmail='[email protected]' - ContactRawData='' - StartTime='11/11/2022 11:51:22 AM' - EndTime='11/11/2022 11:51:39 AM' - EstablishedTime='11/11/2022 11:51:35 AM'
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Processing scenario 'ReportCall'.
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Creating new HttpClient with no authentication
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Skipping request... <-------
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: No matchings found.
2022/11/11 12:51:39.723|22846|0004|Verb|Dequeue Deleted.CONNECTION.15
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Receiver.OnReadyToRead()
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: _sock.Receive == 4
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Received 3153 out of 3153 bytes
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: callOnPacketReceived()
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Received 3153 bytes, start parsing

There are 2 things I got from this log:
  • I see concurrency=1 even in my template I set 10. I would not like to loose cdr records if there are multiple agents receiving calls
  • it seems the http request to the CRM is not made, but I don't understand why
In my CRM I don't receive the CDR request from what I can log.

Thanks
 
  • Like
Reactions: edossantos
Hello @drenda,

Thanks for your reply. I did try with allowEmpty=false in the ReportCall scenario. I enabled verbose logs and I found something interesting:

Code:
022/11/11 12:51:39.722|22846|0046|Info|CRM: about to report 1 calls at maxConcurrency 1 <-------
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Triggering Call Journaling with values - CallType='Inbound' - PhoneNumber='347xxxxxxxxxxx' - ContactName='' - AgentExtension='002' - AgentFirstName='Test' - AgentLastName='Daniele' - AgentEmail='[email protected]' - ContactRawData='' - StartTime='11/11/2022 11:51:22 AM' - EndTime='11/11/2022 11:51:39 AM' - EstablishedTime='11/11/2022 11:51:35 AM'
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Processing scenario 'ReportCall'.
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Creating new HttpClient with no authentication
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: Skipping request... <-------
2022/11/11 12:51:39.722|22846|0046|Verb|CRM: No matchings found.
2022/11/11 12:51:39.723|22846|0004|Verb|Dequeue Deleted.CONNECTION.15
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Receiver.OnReadyToRead()
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: _sock.Receive == 4
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Received 3153 out of 3153 bytes
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: callOnPacketReceived()
2022/11/11 12:51:39.773|22846|0016|Trac|CHR: Received 3153 bytes, start parsing

There are 2 things I got from this log:
  • I see concurrency=1 even in my template I set 10. I would not like to loose cdr records if there are multiple agents receiving calls
  • it seems the http request to the CRM is not made, but I don't understand why
In my CRM I don't receive the CDR request from what I can log.

Thanks
Because there's no contact matched, so EntityId ends up empty and the SkipIf argument of the scenario checks that.

Also, concurrency has nothing to do with losing calls. It's all queued. It's just how many API requests it does at once. You could set that to 2 at least and even more if your CRM API allows it.
 
I see concurrency=1 even in my template I set 10. I would not like to loose cdr records if there are multiple agents receiving calls
Interesting, we will check this. However, please note that you will not lose any record, calls to be logged are queued, this is just to decide how many concurrent requests are processed.

it seems the http request to the CRM is not made, but I don't understand why
This is skipped because there is not a matched contact. The contact lookup must return a matched contact.
 
  • Like
Reactions: Evolute IT
I see concurrency=1 even in my template I set 10. I would not like to loose cdr records if there are multiple agents receiving calls
About this, it's just the log, which is logging a wrong variable. We will fix this in Update 6. But the value you're setting is being honored.
 
Thanks for the quick replies. With your responses I was able to fix the problem. I've still a question about the Lookup scenario.
In our CRM when the lookup doens't find any match, we return an HTTP 404, but I see that this is logged as an error in 3CX logs:

Code:
2022/11/11 15:12:34.194|22846|0024|Erro|CRM: Exception executing contact lookup for number '708xxxx': System.Exception: Server returned a non successful status code - HTTPStatusCode=NotFound - Reason= - Content=
   at CrmIntegration.ScenarioProcessorBase.ProcessRequest(ScenarioRequest request, IValueManager customManager, HttpClient client, CancellationToken token)
   at CrmIntegration.ScenarioProcessorBase.ProcessScenario(ProtocolScenario scenario, IValueManager customManager, CancellationToken token)
   at CrmIntegration.ScenarioProcessorBase.Execute(ProtocolScenario scenario, CancellationToken token)
   at CrmIntegration.CrmProcessor.GetContactInfoFromCrm(List`1 result, IEnumerable`1 scenariosToProcess, IValueManager customManager, Func`2 filterResultsFunctor, Func`1 createContactRecordFunctor, CancellationToken token)
   at CrmIntegration.CrmProcessor.GetContactInfo(String contactNumber, InteractionTypes interactionType, CancellationToken token)

I'd like to know if it's ok our CRM returns HTTP 404 (it should follow the best practice) or if 3CX expects something else when the contact is not found.

Thanks
 
  • Like
Reactions: edossantos
Good that you have it working! :)

Regarding the HTTP status code 404, returning this when no contact is found is a bad practice. A 404 means that the resource is not found, for example that the entity contact is unknown. But when the resource (contacts) exists and no contact can be matched, the proper way to deal with this is returning a 200 OK response, and the body should have an empty JSON, which would indicate that the lookup is not throwing any data. All the CRMs that we support work in this way, and you can be sure that Salesforce, Microsoft, etc. follow proper REST API design guidelines.
 
  • Like
Reactions: Evolute IT
Good that you have it working! :)

Regarding the HTTP status code 404, returning this when no contact is found is a bad practice. A 404 means that the resource is not found, for example that the entity contact is unknown. But when the resource (contacts) exists and no contact can be matched, the proper way to deal with this is returning a 200 OK response, and the body should have an empty JSON, which would indicate that the lookup is not throwing any data. All the CRMs that we support work in this way, and you can be sure that Salesforce, Microsoft, etc. follow proper REST API design guidelines.
Thanks for your reply, we will change this behaviour to match what you described. Thanks for the support
 
Status
Not open for further replies.

Forum statistics

Threads
112,322
Messages
591,889
Members
165,470
Latest member
Robert Lee