Ruby Savon 2 gem: cannot specify XML child and sub-child elements to make valid request to SOAP API Webservice

678 views Asked by At

Have done a huge amount of googling and looked at the Savon 2 documentation but I don't think I am correctly specifying in my ruby code the XML child and sub-child elements I need to run a successful request to this SOAP webservice.

In main2.rb I have tried to use square bracket syntax to go down the document from BookReservationRequest to Booker to UserWithoutALogin. (This square bracket approach worked in another example on the response side). Somehow I am not specifying these properly in Ruby as when I delete these tags in SOAP UI, I get exactly the same error message below. (N.B I am still quite new to Ruby!)

Full error trace:

ruby main2.rb
/Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/response.rb:85:in `raise_soap_and_http_errors!': (soap:Server) System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.NullReferenceException: Object reference not set to an instance of an object. (Savon::SOAPFault)
   at Services.Internal.Helpers.CustomerHelper.GetCustomerIdFromGuestOrBookerType(Nullable`1 partnerServiceId, GuestOrBookerType guestOrBookerType) in c:\TeamCity\LB-QA-04\work\e2ec20c745b940f9\Source\Services\Internal\Helpers\CustomerHelper.cs:line 64
   at Services.Internal.Service.BookReservation(BookReservationRequest bookReservationRequest) in c:\TeamCity\LB-QA-04\work\e2ec20c745b940f9\Source\Services\Internal\Service.asmx.cs:line 289
   --- End of inner exception stack trace ---
    from /Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/response.rb:14:in `initialize'
    from /Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/operation.rb:72:in `new'
    from /Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/operation.rb:72:in `create_response'
    from /Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/operation.rb:58:in `call'
    from /Users/dan14/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/savon-2.11.1/lib/savon/client.rb:36:in `call'
    from main2.rb:11:in `core_info'
    from main2.rb:23:in `<main>'

Ruby code (main2.rb):

require 'savon'

class BookReservation

  def client
    client = Savon.client(wsdl: "http://some-example-soap-wsdl-link", follow_redirects: :follow_redirects)
  end

  def core_info(partner_code, restaurant_location_id, session_id, dining_date_and_time, size)
    message = { 'PartnerCode' => partner_code, 'RestaurantLocationId' => restaurant_location_id, 'SessionId' => session_id, 'DiningDateAndTime' => dining_date_and_time, 'Size' => size }
    response = client.call(:book_reservation, message: message)
    response.hash[:book_reservation_response]
  end

  def booker_info(first_name, last_name, email, guest_accepts_email_marketing)
    message = { 'FirstName' => first_name, 'LastName' => last_name, 'EMail' => email, 'GuestAcceptsEmailMarketingFromPartner' => guest_accepts_email_marketing }
    response = client.call([:book_reservation][:booker][:user_without_a_login], message: message)
    response.body[:book_reservation_response]
  end
end

  book = BookReservation.new
  puts book.core_info("DEV-DAN-BETH:73411", "10799", "DINNER", "2015-06-20T21:00:00", "2", )
  puts book.booker_info("John", "Smith", "[email protected]", "true")

This xml document in SoapUI returns a valid / successful response everytime:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://schemas.livebookings.net/OneFormat/Aggregator/Internal/1/0/">
    <soapenv:Header/>
    <soapenv:Body>      
        <ns:BookReservationRequest>            
            <ns:PartnerCode>DEV-DAN-BETH:73411</ns:PartnerCode>
            <ns:RestaurantLocationId>10799</ns:RestaurantLocationId>
            <ns:SessionId>DINNER</ns:SessionId>
            <ns:DiningDateAndTime>2015-06-20T21:00:00</ns:DiningDateAndTime>
            <ns:Size>2</ns:Size>
            <ns:Booker>
                <ns:UserWithoutALogin>
                    <ns:FirstName>John</ns:FirstName>
                    <ns:LastName>Smith</ns:LastName>
                    <ns:EMail>[email protected]</ns:EMail>                      
                    <ns:GuestAcceptsEmailMarketingFromPartner>true</ns:GuestAcceptsEmailMarketingFromPartner>
               </ns:UserWithoutALogin>
            </ns:Booker>       
        </ns:BookReservationRequest>
    </soapenv:Body>
</soapenv:Envelope>

Example of successful response in SoapUI:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <BookReservationResponse xmlns="http://schemas.livebookings.net/OneFormat/Aggregator/Internal/1/0/">
         <ConfirmationNumber>T2NVGBUN</ConfirmationNumber>
         <ReservationId>34277666</ReservationId>
         <AllowedToCancelOnline>true</AllowedToCancelOnline>
      </BookReservationResponse>
   </soap:Body>
</soap:Envelope>

Thanks very much in advance!

1

There are 1 answers

6
mcfinnigan On BEST ANSWER

Your SOAP message example contains a Body that contains a Book Reservation Request that includes a Booker block.

your BookReservation class, however, contains two methods that call the Savon client - one of these sends a BookReservationRequest - but you never add the Booker to the message hash before dispatching it.

It's almost 100% certain that your client is sending an invalid SOAP request because you have not specified the <ns:Booker> block in your SOAP body.

I suspect that if you updated your class to contain one method that looked as follows:

def execute(partner_code, restaurant_location_id, session_id, dining_date_and_time, size, first_name, last_name, email, guest_accepts_email_marketing)
    message = { 'PartnerCode' => partner_code, 'RestaurantLocationId' => restaurant_location_id, 'SessionId' => session_id, 'DiningDateAndTime' => dining_date_and_time, 'Size' => size }
    message.merge!('Booker' => { 'FirstName' => first_name, 'LastName' => last_name, 'EMail' => email, 'GuestAcceptsEmailMarketingFromPartner' => guest_accepts_email_marketing })

    response = client.call(:book_reservation, message: message)
    response.hash[:book_reservation_response]

end

And called it with:

book.execute(
  "DEV-DAN-BETH:73411", "10799", "DINNER", "2015-06-20T21:00:00", "2",
   "John", "Smith", "[email protected]", "true" )

You'd have better luck.