Rails WebMock against XML request - ruby-on-rails

I am having a hard time figuring out how to match stub_request with XML requests using webmock.
Request:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header>
<RequesterCredentials xmlns="urn:ebay:api:PayPalAPI" xmlns:n1="urn:ebay:apis:eBLBaseComponents" env:mustUnderstand="0">
<n1:Credentials>
<n1:Username>XYZ</n1:Username>
<n1:Password>ABC</n1:Password><n1:Subject/>
<n1:Signature>HUGE-STRING</n1:Signature>
</n1:Credentials>
</RequesterCredentials>
</env:Header>
<env:Body>
<ManageRecurringPaymentsProfileStatusReq xmlns="urn:ebay:api:PayPalAPI">
<ManageRecurringPaymentsProfileStatusRequest xmlns:n2="urn:ebay:apis:eBLBaseComponents">
<n2:Version>124</n2:Version>
<n2:ManageRecurringPaymentsProfileStatusRequestDetails>
<ProfileID>sdaddsadsd</ProfileID>
<n2:Action>Cancel</n2:Action>
</n2:ManageRecurringPaymentsProfileStatusRequestDetails>
</ManageRecurringPaymentsProfileStatusRequest>
</ManageRecurringPaymentsProfileStatusReq>
</env:Body>
</env:Envelope>
Stubs I tried:
stub_request(:any, /.*.sandbox.paypal.com\/2.0\//).
with(:body => WebMock::Matchers::HashIncludingMatcher.new({'n2:Action'=>'Cancel'}),
:headers => {'User-Agent'=>'Ruby'}).
to_return(:status => 200, ...
or
stub_request(:any, /.*.sandbox.paypal.com\/2.0\//).
with(query:
hash_including({ProfileID: 'sdaddsadsd'}),
:headers => {'User-Agent'=>'Ruby'}).
to_return(:status => 200, ...
(etc.)
have failed.
Anyone?

Provided you can save your XML to be stubbed into separate XML file (say request.xml), it is quite easy:
stub_request(:any, 'https://sandbox.paypal.com')
.with(body: File.read('request.xml').strip)
Headers can be omitted.
How to use webmock regex matcher? might help you with URL matching.
P.S. strip is to remove ending line feed

Related

Savon using built in request builder instead of raw xml

If i use savon with raw xml everything works fine, this is the raw xml example:
request = client.call(:authenticate, xml:'<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="xxxx/">
<SOAP-ENV:Body>
<ns1:authenticate>
<ns1:username>user</ns1:username>
<ns1:password>pwd</ns1:password>
<ns1:cultureInfo>it</ns1:cultureInfo>
</ns1:authenticate>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>')
If I use the builtin method to call the method I got an error, this is the code:
credentials={ username: 'user', password: 'pwd!!', cultureInfo: "it" }
response = client.call(:authenticate, message: credentials)
This is the xml produced by the above code:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://tempuri.org/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wsdl:authenticate>
<username>user</username>
<password>pwd</password>
<cultureInfo>it</cultureInfo>
</wsdl:authenticate>
</env:Body>
</env:Envelope>
Any Idea?
I usually do just this (untested). It's not pretty but it works.
credentials = { 'ns1:username' => 'user',
'ns1:password' => 'pwd!!',
'ns1:cultureInfo' => "it" }
response = client.call(:authenticate, message: credentials)
You might want to adapt the use of ns1 to the actually used namespace in your case.

How to remove headers set by Savon?

I need to hit a SOAP service, however, the server won't accept my request but throws an error due to the below header and footer in the request.
As per the logs, the request/response looks like below:
SOAP **request**: http://www.url.co.au/wserv/wslms.php
SOAPAction: "insertlmsdata", Content-Type: text/xml;charset=UTF-8, Content-Length: 938
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="urn:lmsdataautomation" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<tns:insertlmsdata>
<lmsinfo>
<userCred>
<clientCode>code</clientCode>
<username>name</username>
<password>ee##!</password>
</userCred>
<lead>
<requestno>3</requestno>
<firstname>Meckinsey</firstname>
<lastname>Meth</lastname>
<email>meckinsey.meth#er.com</email>
<mobileno>4233333333</mobileno>
<keyword>Romali</keyword>
<requestMessage>looking for Romali</requestMessage>
<responseMessage>Romadd</responseMessage>
<operator>you</operator>
<circle>Romaon</circle>
<requestDate>20-04-2017</requestDate>
<shortCode>2222</shortCode>
<category>DOM</category>
<city>Sydney</city>
<destination>Perth</destination>
</lead>
</lmsinfo>
</tns:insertlmsdata>
</env:Body>
</env:Envelope>
HTTPI POST request to www.egggo.co.au (excon)
SOAP **response** (status 200)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:lmsdataautomation" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:insertlmsdataResponse>
<output xsi:type="xsd:string"><output>Either parsed xml is not valid or contains special chars like & "'</output></output>
</ns1:insertlmsdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have looked thoroughly Savon Documentation, couldn't find anything relevant. Can I configure Savon to not add headers, footer, envelope and just the body?
Basically to ignore the below data:
SOAP request: http://www.url.co.au/wserv/wslms.php
SOAPAction: "insertlmsdata", Content-Type: text/xml;charset=UTF-8, Content-Length: 938
Otherwise, the SOAP server rejects the request. Please help, TIA
Adding the body of the request:
client = Savon.client(wsdl: url, log: true, logger: Rails.logger, log_level: :debug, pretty_print_xml: true, encoding: 'UTF-8')
response = client.call(:insertlmsdata, message: {'lmsinfo'=> {'userCred'=> {'clientCode' => 'code', 'username' => 'dom', 'password' => '#pass' }, 'lead' => {'requestno' => 3, 'firstname'=>'Methyl', 'lastname'=> 'Orange', 'email' => 'methyl.orange#egg.com', 'mobileno' => '55555555', 'keyword' => 'atlanta', 'requestMessage' => 'looking for atlant', 'responseMessage' => 'dd', 'operator' => 'you', 'circle' => 'sydney', 'requestDate' => '04-20-2017', 'shortCode' => 2222, 'category'=> 'DOM', 'city' => 'Sydney', 'destination' => 'Atlanta' } } })

Soap client request code for Savon 2.5.1

I am working with the Savon gem (version 2.5.1) for creating a Soap request and need if to be formatted as follows:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:soap1="urn:website.co.uk/soap:callreport7">
<soap:Header>
<soap1:callcreditheaders>
<soap1:company>?</soap1:company>
<soap1:username>?</soap1:username>
<soap1:password>?</soap1:password>
</soap1:callcreditheaders>
</soap:Header>
<soap:Body>
<soap1:Test07a>?</soap1:Test07a>
</soap:Body>
</soap:Envelope>
I have so far come up with the following:
client = Savon.client(
wsdl: "CallReport7.wsdl",
env_namespace: :soap,
soap_header: {
"soap1:callcreditheaders" => {
'soap1:company' => 'XXXX',
'soap1:username' => 'XXXX',
'soap1:password' => 'XXXX'
}
}
)
response = client.call(:test07a, message: "Test" )
This is returning an error when running in the console of:
Savon::SOAPFault: (soap:Server) An internal system error has occurred
If I remove the soap_header hash then the test runs and returns:
<?xml version="1.0" encoding="utf-8"?>
<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>
<Test07aResponse xmlns="website.co.uk/soap:callreport7">
<TestResult>
<datetime>2014-06-06T14:18:35.081125+01:00</datetime>
</TestResult>
</Test07aResponse>
</soap:Body>
</soap:Envelope>
The problem is I need the header information and I cannot work out how to format the request for this.
As usual: First create a valid request using SoapUI. Then build the equivalent (doesn't have to be exact the same) in Savon.
It turned out the problem was with the envelope. I enabled logging in my code so I could see the SOAP produced by Savon and this then allowed me to try the request in SoapUI and do some trial and error testing. Turned out the problem was with the Envelope so the below fixed the problem in my case:
client = Savon.client(
wsdl: "CallReport7.wsdl",
pretty_print_xml: true,
log: true,
env_namespace: :soap,
namespaces: {"xmlns:soap" => "http://www.w3.org/2003/05/soap-envelope", "xmlns:soap1" => "urn:website.co.uk/soap:callreport7"},
soap_header: {
"soap1:callcreditheaders" => {
'soap1:company' => 'XXXX',
'soap1:username' => 'XXXX',
'soap1:password' => 'XXXX'
}
}
)
response = client.call(:test07a, message: "Test" )
The logging line of code was the key to solving this problem for me.

How to organize the syntax in this SOAP request packet sent with gem Savon in a ruby script?

I am trying to send a SOAP request to a resource on the web. It is expecting to receive this:
<?xml version="1.0" encoding="utf-8"?>
<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>
<DiscoverParameterValues xmlns="http://comscore.com/">
<parameterId>loc</parameterId>
<query xmlns="http://comscore.com/ReportQuery">
<Parameter KeyId="geo" Value="124" />
</query>
</DiscoverParameterValues>
</soap:Body>
</soap:Envelope>
This is the Ruby code that Im using to send a request that will result in an XML similar to the one above.
require 'savon'
#Connect to the Comscore API
client = Savon.client(
wsdl:"https://api.comscore.com/KeyMeasures.asmx?WSDL",
basic_auth: ["username", "pw" ],
log: true, :pretty_print_xml=>true)
And this is the XML thats generated:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://comscore.com/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ins0="http://comscore.com/Report"
xmlns:ins1="http://comscore.com/ReportQuery"
xmlns:ins2="http://comscore.com/ReportModel"
xmlns:ins3="http://comscore.com/FetchMedia"
xmlns:ins4="http://comscore.com/Media/Response">
<env:Body>
<tns:DiscoverParameterValues>
<tns:parameterId>loc</tns:parameterId>
==> <tns:query>
<tns:Parameter>
<tns:attributes>
<tns:KeyId>geo</tns:KeyId>
<tns:Value>124</tns:Value>
</tns:attributes>
</tns:Parameter>
==> </tns:query>
</tns:DiscoverParameterValues>
</env:Body>
</env:Envelope>
As you can see there is a difference in how the XML from my code and the XML thats required looks. Ive marked it with "=>"
Here is the gem documentation page that Im using to structure my query:
http://savonrb.com/version2/locals.html
Ive tried playing around with this but I dont understand how to get the "attributes" to work right.
Any suggestions?
UPDATE:
After reading this post, I changed my ruby syntax to the following:
attributes = { "KeyId"=>"geo", "Value" =>"124"}
parameter = {
:parameterId=>"loc",
:query=>{ "Parameter" => { attributes: attributes}}
}
response = client.call(:discover_parameter_values,
message:{:parameterId=>"loc",
:query =>{ "Parameter"=> "", :attributes! =>
{ "Parameter" =>
{ "KeyId" => "geo" , "Value"=>"124" }}},
},
:attributes => {"xmlns" => "http://comscore.com/" })
My XML changed to this:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://comscore.com/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ins0="http://comscore.com/Report"
xmlns:ins1="http://comscore.com/ReportQuery"
xmlns:ins2="http://comscore.com/ReportModel"
xmlns:ins3="http://comscore.com/FetchMedia"
xmlns:ins4="http://comscore.com/Media/Response">
<env:Body>
<tns:DiscoverParameterValues xmlns="http://comscore.com/">
<tns:parameterId>loc</tns:parameterId>
<tns:query>
<tns:Parameter KeyId="geo" Value="124"/>
</tns:query>
</tns:DiscoverParameterValues>
</env:Body>
</env:Envelope>
So everything works EXCEPT - I cannot set "attributes" on the "query" tag.
I would like to set this as an attribute on the query: "xmlns=http://comscore.com/ReportQuery"
How do I do that?
Question: Does it work?
Savon uses the namespace qualifiert in a different way than your first example.
In the following snippet the tags within have an implicit namespace as specified in the attribute xmlns.
<DiscoverParameterValues xmlns="http://comscore.com/">
<parameterId>loc</parameterId>
<query xmlns="http://comscore.com/ReportQuery">
<Parameter KeyId="geo" Value="124" />
</query>
</DiscoverParameterValues>
Savon uses a different syntax. It defines all needed namespace in the envelope and uses the prefixes per tag later. Different syntax for the same expression.
SoapUI uses often the former variant, Savon the latter.
ParameterIdcomes in both cases from the namespace http://comscore.com/.
UPDATE response:
You can go different ways:
Is usually cheat: I just write fully qualified keys, eg.
...
:query => { "Parameter" => "", :attributes! =>
{ "Parameter" =>
{ "ins1:KeyId" => "geo", "ins1:Value" => "124" }}},
...
If you absolutely want to put the key as attribute on "query" then you can define it like this:
response = client.call(:discover_parameter_values,
message:{:parameterId=>"loc",
:query =>{ "Parameter"=> "", :attributes! =>
{ "Parameter" =>
{ "KeyId" => "geo" , "Value"=>"124" }}},
:attributes! => {'tns:query' =>
{ "xmlns" => "http://comscore.com/ReportQuery" }
},
:attributes => {"xmlns" => "http://comscore.com/" })

Savon + Rails 2 How to modify XML's request structure

I'm using Savon to develop a Web Service Client. Since I'm a beginner I decided to try at first with an example WDSL, which in my case is:
http://www.webservicex.com/CurrencyConvertor.asmx?wsdl
My controller is very simple:
require 'savon'
class WebServiceController < ApplicationController
def index
puts "web_service: IN"
client = Savon::Client.new do
wsdl.document = "http://www.webservicex.com/CurrencyConvertor.asmx?wsdl"
end
response = client.request :conversion_rate do
soap.body = {
:from_currency => 'USD',
:to_currency => 'EUR'
}
end
puts response.to_hash;
render :text => response.to_hash
end
end
The XML produced by that code is:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://www.webserviceX.NET/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<ConversionRate>
<wsdl:fromCurrency>USD</wsdl:fromCurrency>
<wsdl:toCurrency>EUR</wsdl:toCurrency>
</ConversionRate>
</env:Body>
</env:Envelope>
However, the XML should be (and I know this because I'm using soapUI):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://www.webserviceX.NET/">
<soapenv:Header/>
<soapenv:Body>
<web:ConversionRate>
<web:FromCurrency>USD</web:FromCurrency>
<web:ToCurrency>EUR</web:ToCurrency>
</web:ConversionRate>
</soapenv:Body>
</soapenv:Envelope>
I know my XML Request isn't working because I always get '0' (zero) as response, and with the "right" XML Request generated by soapUI I get correct values (such as '0.6959' ...).
Is something missing in my code?
Thank you!!!
two things:
you need to add :wsdl to the call
you need to make sure the spelling of the tags is correct
change to
response = client.request :wsdl, :conversion_rate do
and to
"FromCurrency" => 'USD',
"ToCurrency" => 'EUR'
that should do it for you.

Resources