Reputation: 95
I'm working on a project that requires working with a SOAP api... and really liked the look of Savon for tackling this. This is my first time working with an API outside of the code academy tutorial...
Long story short, no matter what I do... the third party API kept saying bad api key... as their error reporting is pretty weak. I had the code for all of that here - but have removed it after learning to install a gem to log outgoing http requests. In doing so, I've gotten to the source of the problem... and could use some guidance.
In short - Savon is not generating the same outgoing XML that SOAPUI is generating.
Using SOAPUI (what we want...)
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sec="http://secure.treasury.exchange/">
<soapenv:Header/>
<soapenv:Body>
<sec:GetAccountBalance soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<GetAccountBalanceRequest xsi:type="sec:GetAccountBalanceRequest">
<!--You may enter the following 2 items in any order-->
<ApiKey xsi:type="xsd:string">xxx</ApiKey>
<AccountNumber xsi:type="xsd:string">123</AccountNumber>
</GetAccountBalanceRequest>
</sec:GetAccountBalance>
</soapenv:Body>
</soapenv:Envelope>
This is what Savon is generating
<?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="http://secure.treasury.exchange/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<tns:GetAccountBalance>
<apiKey>xxx</apiKey>
<accountNumber>123</accountNumber>
</tns:GetAccountBalance>
</env:Body>
</env:Envelope>
I'm assuming there are some variables I need to set in the config to bridge Savon over to what SOAP UI is doing...
Any advice?
UPDATE: Dirty solution found.
https://www.reddit.com/r/ruby/comments/289wfn/soap_issues_with_savon/
In a nutshell, you can define an xml: variable... which you can use to define the exact xml you want savon to request. It doesn't look pretty, but at least it's working for now.
I'll be keeping an eye out for a better solution.
Upvotes: 1
Views: 893
Reputation: 16793
In order to get the XSI attributes to display correctly, I found I had to format them in the following way that I couldn't find much documentation for: '@xsi:type' => 'xsd:string'
.
So, for the following XML:
<GetAccountBalanceRequest xsi:type="sec:GetAccountBalanceRequest">
<ApiKey xsi:type="xsd:string">xxx</ApiKey>
<AccountNumber xsi:type="xsd:string">123</AccountNumber>
</GetAccountBalanceRequest>
I would wager you'd probably want a hash that looked something like the following to use in your SOAP call:
{
'GetAccountBalanceRequest' => {
'@xsi:type' => 'sec:GetAccountBalanceRequest',
'ApiKey' => {
'@xsi:type' => 'xsd:string',
'ID' => 'xxx'
},
'AccountNumber' => {
'@xsi:type' => 'xsd:string',
'ID' => '123'
}
}
}
Not sure about the extra ID
hashes I put in; you may not need them and can instead just put the ID value, but last time I used this the XSI type values needed to be a hash of hashes.
In order to get the attributes displaying properly, you need to go and have a look at the Gyoku gem, the gem that Savon uses to translate Ruby hashes into XML. Specifically, the documentation on using explicit XML attributes. Looking at that, we can get the XML you're looking for with the following hash:
{
"sec:GetAccountBalance" => {
"@soapenv:encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/",
"GetAccountBalanceRequest" => {
"@xsi:type" => "sec:GetAccountBalanceRequest",
"ApiKey" => {
"@xsi:type" => "xsd:string",
:content! => 'xxx'
},
"AccountNumber" => {
"@xsi:type" => "xsd:string",
:content! => "123"
}
}
}
}
This is testable via a simple Ruby script as well:
hash_to_xml.rb
require 'gyoku'
puts Gyoku.xml(
{
"sec:GetAccountBalance" => {
"@soapenv:encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/",
"GetAccountBalanceRequest" => {
"@xsi:type" => "sec:GetAccountBalanceRequest",
"ApiKey" => {
"@xsi:type" => "xsd:string",
:content! => 'xxx'
},
"AccountNumber" => {
"@xsi:type" => "xsd:string",
:content! => "123"
}
}
}
}
)
And then run it:
$ ruby hash_to_xml.rb
# =>
# <sec:GetAccountBalance soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
# <GetAccountBalanceRequest xsi:type="sec:GetAccountBalanceRequest">
# <ApiKey xsi:type="xsd:string">xxx</ApiKey>
# <AccountNumber xsi:type="xsd:string">123</AccountNumber>
# </GetAccountBalanceRequest>
# </sec:GetAccountBalance>
Upvotes: 1
Reputation: 11235
Try using string keys for your hash:
client.call(:get_account_balance, message: { "ApiKey" => "XXX", "AccountNumber" => "1234" })
Upvotes: 1