0

I have a Perl script that is successfully getting a response from my ShoreTel Phone server. The server provides information on what calls are currently connected for the extension entered. However I am having issues looping through the sub arrays to get more than one response when there are multiple items. In this case I want to get each of the caller IDs that is currently connected.

My SOAP:LITE request is successfully pulling data from the server using the following code:

use strict;
use warnings;
use SOAP::Lite;
use CGI;
use Data::Dumper;

my $myWebService = SOAP::Lite
  -> uri('http://www.ShoreTel.com/ProServices/SDK/Web')
  -> proxy('http://10.1.##.##:8070/ShoreTelWebSDK/WebService')
  -> on_action(sub {sprintf '%s/ShoreTelWebService/%s', $_[0], $_[1]});

my $query = new CGI;
my $ip = $query->remote_host;                    # IP address of remote party...use later as unique identifier
my $myClientID = $query->param('MyClientID');    # Possible client ID from previous script passed into us.
my $extnNr = $query->param('MyExtn');            # Has to be at least an extension number so we know who to status.
my $url = CGI::url(-path_info=>1);               # What is my URL?

# There should be an extension number given, else what would we status.

if (defined($refreshNr) &&  defined($extnNr) && ($extnNr ne '') && ($refreshNr ne ''))
{
    # If there is a client ID defined, use it...otherwise registering and getting a client ID
    # is the first thing we need to do when using our web service.

  unless (defined($myClientID))
  {
    # To use our service, we need to register ourselves as a client...use remote IP address
    # as a unique name for association to this session.

    my $regClientResult = $myWebService->RegisterClient(SOAP::Data->name('clientName' => $ip));
    if ($regClientResult->fault)
    {
      print '<p>FAULT', $myClientID->faultcode, ', ', $myClientID->faultstring;
    }
    else
    {
      # Retrieve client ID which we will be using for subsequent communication.

      $myClientID = $regClientResult->valueof('//RegisterClientResponse/RegisterClientResult/');
    }
  }

   if (defined($myClientID))
  {
    # Use our web service to open the line.  This is necessary to get a line ID.

    # print '<br>Client ID ', $myClientID, ' has been registered.<br>';

    my $openResult = $myWebService->OpenLine(SOAP::Data->name('clientHandle' => $myClientID), SOAP::Data->name('lineAddress' => $extnNr));
    my $lineID = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineID/');
    my $lineType = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineType/');
    my $lineName = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineName/');
    my $lineState = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineState/');

# Call GetActiveCalls to see if anything is going on with this line.

    my $result = $myWebService->GetActiveCalls(SOAP::Data->name('clientHandle' => $myClientID), SOAP::Data->name('lineID' => $lineID));

    my $callID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callID/');

if ($callID ne '')
    {
      # print '<br>Call ID is ', $callID;

      my $isExternal = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/isExternal/');
      my $isInbound = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/isInbound/');
      my $callReason = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callReason/');
      my $connectedID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/connectedID/');
      my $connectedIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/connectedIDName/');
      my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');
      my $callerIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerIDName/');
      my $calledID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/calledID/');
      my $calledIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/calledIDName/');

      my $callState = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callState/');
      my $callStateDetail = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callStateDetail/');

      # Print call information.

      print <<EndOfCallInfo;
      HTML CODE
EndOfCallInfo

    }
    else
    {
         print <<EndOfCallInfo2;
         HTML CODE

EndOfCallInfo2
    }
  }

}

But I am only able to access the first result in the multidimensional array.
I have tried looping through the results using

for my $t ($result->result({ShoreTelCallStateInfo}{callInfo}')) {
    print $t->{callerID} . "\n";}

But I am getting absolutely no results. It appears that the the loop is not even entered.

The following code I have works fine, but only pulls the first caller ID, in this case 1955.

my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');

What can I do to make my loop work?

So that you can see what I am receiving from the server I have included the response from the SOAP Server using DUMP :

$VAR1 = { 'ShoreTelCallStateInfo' => [ 
            { 'callStateDetail' => 'Active', 
              'callState' => 'OnHold', 
              'callInfo' => 
                { 'callerIDName' => 'Joel LASTNAME', 
                  'callID' => '69105', 'lineID' => '3947', 
                  'connectedIDName' => 'VM-Forward', 
                  'calledID' => '2105', 
                  'callerID' => '1955', 
                  'isInbound' => 'false', 
                  'calledIDName' => 'VM-Forward', 
                  'callReason' => 'None', 
                  'callUniqueID' => '1369702515', 
                  'connectedID' => '2105', 
                  'isExternal' => 'false', 
                  'callGUID' => '{00030000-66C2-537E-3FD8-0010492377D9}' 
                 } 
            }, 
            { 'callStateDetail' => 'Active', 
              'callState' => 'Connected', 
              'callInfo' => 
                { 'callerIDName' => 'LASTNAME Joel ', 
                  'callID' => '71649', 
                  'lineID' => '3947', 
                  'connectedIDName' => 'LASTNAME Joel ', 
                  'calledID' => '1955', 
                  'callerID' => '+1385#######', 
                  'isInbound' => 'true', 
                  'calledIDName' => 'Joel LASTNAME', 
                  'callReason' => 'None', 
                  'callUniqueID' => '1117287558', 
                  'connectedID' => '+1385#######', 
                  'isExternal' => 'true', 
                  'callGUID' => '{00030000-66C5-537E-3FD8-0010492377D9}' 
                } 
            } 
        ] 
        }; 
June Lewis
  • 355
  • 1
  • 6
  • 28
  • What do you get if you use `Data::Dumper` and `print Dumper \$t` in your first example? Is it an array reference? – Sobrique Mar 31 '15 at 14:11
  • @Sobrique I get the same error "[error] Method 'result' is readonly and doesn't accept any parameters at " – June Lewis Mar 31 '15 at 14:14
  • 1
    OK. And that dump? What's it a dump _of_? – Sobrique Mar 31 '15 at 14:14
  • @sobrique it is a dump of $result. – June Lewis Mar 31 '15 at 14:17
  • OK, cool. In which case my answer below may do the trick. (e.g. skipping the 'SOAP' accessors and just treating it as a perl data structure) – Sobrique Mar 31 '15 at 14:18
  • I started to "fix" your question, and didn't get past the subject line before I was unsure that my changes would be correct. As the warning message says, you are calling the `result` method with parameters that it doesn't expect, so you are simply misunderstanding the SOAP API. It also worries me that XPath expressions like `//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/` are missing slashes. Presumably you meant `//GetActiveCalls/Response/GetActiveCalls/Result/...` and beyond that I cannot guess. – Borodin Mar 31 '15 at 15:28
  • 1
    I suggest that you should describe what it is that you're trying to *achieve* rather than how your proposed solution has gone wrong. SOAP has a famously awkward API, and I am not surprised you fell foul of it – Borodin Mar 31 '15 at 15:30
  • @Borodin The XPath expressions are not missing slashes, these are the headers that are being provided by the server i am calling. I am hoping to get some help understanding the SOAP api so that I can make this work. – June Lewis Mar 31 '15 at 15:30
  • @Sobrique: *"my answer below may do the trick—skipping the 'SOAP' accessors and just treating it as a Perl data structure"* Please *never recommend such nastiness. SOAP—the Simple Object Access Protocol—is meant as a standard that enables dialogue between peers. If you jink that to "get it working" then you haven't implemented SOAP, and I am surprised at you for suggesting it – Borodin Mar 31 '15 at 15:34
  • True enough. Tried the best I could given some sketchy initial information. – Sobrique Mar 31 '15 at 15:36
  • @JoelLewis: You are probably looking at the output of `print` when given the object's keys. Use `say for` or `print "$_\n" for` and you will see the difference. – Borodin Mar 31 '15 at 15:39
  • You start by removing the parameter to the `result` call. It doesn't belong there. And then you write a new question on Stack Overflow that describes in detail what it is that you want to achieve, and what you have done so far. You shouldn't dump the contents of any objects, but instead use the API defined in [SOAP::Lite](https://metacpan.org/pod/SOAP::Lite). That way you won't be expecting things that aren't provided, or ignoring things that are – Borodin Mar 31 '15 at 15:40
  • I noticed your edit. But why are you still calling `result` with a parameter when the module tells you that it doesn't accept one? I hoped you would fix that error and move on to correct the rest, but it is still there. You alone have the test environment, so we can't really help much if you don't accept the error messages and fix them – Borodin Mar 31 '15 at 15:48
  • @Sobrique: *"True enough. Tried the best I could given some sketchy initial information"* Please don't do that! It is difficult enough to get questions described properly as it is without reputable people like yourself *proving* that we don't need such a thorough explanation of the problem! – Borodin Mar 31 '15 at 15:57
  • @Borodin I have added more detail as well as adjusted my attempt. Please let me know how I can better explain my problem if it is still not clear. – June Lewis Mar 31 '15 at 16:02
  • Oh dear. I can't fathom what your meaning is, and I can't write something equivalent. You should use *relative* references where possible: for instance `//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo` is repeated ten times, and makes your code unreadable. I think your bug is because you can't see through your own code, and your priority should be to make it as clear as possible – Borodin Mar 31 '15 at 17:33

1 Answers1

1

Just a guess...

The following code I have works fine, but only pulls the first caller ID, in this case 1955.

my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');

What can I do to make my loop work?

SOAP::Lite docs say:

valueof()

Returns the value of a (previously) matched node. It accepts a node path. In this case, it returns the value of matched node, but does not change the current node. Suitable when you want to match a node and then navigate through node children:

  $som->match('/Envelope/Body/[1]'); # match method
  $som->valueof('[1]');              # result
  $som->valueof('[2]');              # first out parameter (if present)

The returned value depends on the context. In a scalar context it will return the first element from matched nodeset. In an array context it will return all matched elements.

Does this give the behavior you expect? It imposes list context on the valueof method.

for my $callerID ($result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/')) {
      ...
      # do something with each callerID
}

or

my @callerIDs = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');
Oesor
  • 6,632
  • 2
  • 29
  • 56