How to make a SOAP call including namespace with Savon - ruby

I am trying to connect to this SOAP API. Specifically, I am trying to login via this login call.
The documentation states that I should make the following request:
POST /service/replicatorV4.asmx HTTP/1.1
Host: demo12231.srv106.webshopdemo.net
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/Login"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Login xmlns="http://tempuri.org/">
<username>string</username>
<password>string</password>
</Login>
</soap:Body>
</soap:Envelope>
When I in e.g. SoapUI makes the following request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:Login>
<tem:username>Administrator</tem:username>
<tem:password>passw0rd12</tem:password>
</tem:Login>
</soapenv:Body>
</soapenv:Envelope>
then I get this satisfying response:
<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>
<LoginResponse xmlns="http://tempuri.org/">
<LoginResult>48594fe6-41fd-45f9-9f84-89ef8c247b85</LoginResult>
</LoginResponse>
</soap:Body>
</soap:Envelope>
However, when I with Savon make a request like this:
require 'savon'
client = Savon.client( :wsdl => "http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
:open_timeout => 100,
:read_timeout => 100,
:ssl_verify_mode => :none,
:log_level => :debug,
:log => false,
:logger => Rails.logger,
:convert_request_keys_to => :camelcase)
response = client.call(:login, :message => {:username => "Administrator", :password => "passw0rd12"})
then I get this dissatisfying response:
#<Savon::Response:0x007ff78cfcc368
#globals=
#<Savon::GlobalOptions:0x007ff782fb8160
#option_type=:global,
#options=
{:encoding=>"UTF-8",
:soap_version=>1,
:namespaces=>{},
:logger=>
#<ActiveSupport::Logger:0x007ff78b136ea8
#default_formatter=
#<Logger::Formatter:0x007ff78b136e30 #datetime_format=nil>,
#formatter=
#<ActiveSupport::Logger::SimpleFormatter:0x007ff783e99f80
#datetime_format=nil>,
#level=0,
#logdev=
#<Logger::LogDevice:0x007ff78b136de0
#dev=
#<File:/Users/me/Projects/my_project/log/development.log>,
#filename=nil,
#mutex=
#<Logger::LogDevice::LogDeviceMutex:0x007ff78b136db8
#mon_count=0,
#mon_mutex=#<Mutex:0x007ff78b136d68>,
#mon_owner=nil>,
#shift_age=nil,
#shift_size=nil>,
#progname=nil>,
:log=>false,
:filters=>[],
:pretty_print_xml=>false,
:raise_errors=>true,
:strip_namespaces=>true,
:convert_response_tags_to=>
#<Proc:0x007ff782fc3e70#/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/savon-2.11.1/lib/savon/options.rb:85 (lambda)>,
:convert_attributes_to=>
#<Proc:0x007ff782fc3df8#/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/savon-2.11.1/lib/savon/options.rb:86 (lambda)>,
:multipart=>false,
:adapter=>nil,
:use_wsa_headers=>false,
:no_message_tag=>false,
:follow_redirects=>false,
:unwrap=>false,
:host=>nil,
:wsdl=>
"http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
:open_timeout=>100,
:read_timeout=>100,
:ssl_verify_mode=>:none,
:convert_request_keys_to=>:camelcase,
:endpoint=>
#<URI::HTTP:0x007ff78479cbf0 URL:http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx>}>,
#http=
#<HTTPI::Response:0x007ff78cfcd2b8
#body=
"<?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><LoginResponse xmlns=\"http://tempuri.org/\"><LoginResult xsi:nil=\"true\" /></LoginResponse></soap:Body></soap:Envelope>",
#code=200,
#headers=
{"Cache-Control"=>"private, max-age=0",
"Content-Type"=>"text/xml; charset=utf-8",
"Server"=>"Microsoft-IIS/8.5",
"Date"=>"Thu, 27 Aug 2015 05:29:42 GMT",
"Content-Length"=>"335"},
#raw_body=
"<?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><LoginResponse xmlns=\"http://tempuri.org/\"><LoginResult xsi:nil=\"true\" /></LoginResponse></soap:Body></soap:Envelope>">,
#locals=
#<Savon::LocalOptions:0x007ff78ce7f550
#option_type=:local,
#options=
{:advanced_typecasting=>true,
:response_parser=>:nokogiri,
:multipart=>false,
:message=>{:username=>"Administrator", :password=>"passw0rd12"},
:soap_action=>"http://tempuri.org/Login"}>>
The body part of the response is:
{:login_response=>{:login_result=>nil, :#xmlns=>"http://tempuri.org/"}}
and this tells me that maybe the service did receive the overall request, but did not get the variables. The response looks a lot like the response when I in SoapUI make this request with wrong username and password:
<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>
<LoginResponse xmlns="http://tempuri.org/">
<LoginResult xsi:nil="true"/>
</LoginResponse>
</soap:Body>
</soap:Envelope>
I have noticed that the namespace tem is expected in variables. Could this have something to do with my issue? Or is there another explanation?

Create a pure Ruby script without Rails.
then check the output against what you've created with SoapUI. Post the differences and someone will be able to help.
when I run this script:
require 'savon'
c = Savon.client(wsdl:
"http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
log_level: :debug,
log: true,
pretty_print_xml: true)
response = c.call(:login,
:message => {:username => "Administrator", :password => "passw0rd12"})
it works for me. It might be something in your Rails part which interferes. I don't do Rails therefore I'll be of little help there (I prefer Sinatra :-)).

Related

Add attribute in Savon request

How can I add the attribute 'updateVersion' in the request, when using the following format:
response = client.call(
:method, :message => { extID => 'X-1234', ..., '#updateVersion' => '0' }
)
I need to set the updateVersion to 0 for my call to be successful and of course the extID.
When using response = client.call( :method, xml: "xml") then it is working as expected, but I'd like to see/test the other option.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://www.url">
<soapenv:Body>
<int:policyInList>
<!--0 to 1000 repetitions:-->
<int:policyID main="true" system_id="0" updateVersion="?">
<extID>X-1234</extID>
</int:policyID>
</int:policyInList>
</soapenv:Body>
</soapenv:Envelope>

Close AccountingPeriod in Zuora via SOAP

I am trying to call the update method of the Zuora Soap API so as to 'CloseAccountingPeriod'. What is the correct way to do this
require 'savon'
message = {'username' => 'username','password' => 'password' }
client = Savon.client(wsdl: 'zuora.a.75.0.wsdl')
response = client.call(:login, message: message)
#response = client.call :update do
soap.header = {
}
soap.body = {
:Status=> "closed",
:ids=>"4028e699235ea4de0123777131fd5d68"
}
end
The Zuora Soap looks like this:
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv=
"http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<ns1:SessionHeader xmlns:ns1=
"http://api.zuora.com/" soapenv:mustUnderstand="0">
<ns1:session>[replace with your session]</ns1:session>
</ns1:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<ns1:update xmlns:ns1="http://api.zuora.com/">
<ns1:zObjects xmlns:ns2="http://object.api.zuora.com/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns2:AccountingPeriod">
<ns2:Id>402892a83711418b01371142cc5801a9</ns2:Id>
<ns2:Status>Closed</ns2:Status>
</ns1:zObjects>
</ns1:update>
</soapenv:Body>
</soapenv:Envelope>
That example is correct
The minimum required data is
session key
32 char id of the AccountingPeriod
Status "Closed"

Server did not recognize the value of HTTP Header SOAPAction with net/http

I want to use the GetInfoByZIP servide of this WSDL http://www.webservicex.net/uszip.asmx?WSDL using net/http but always get the error "Server did not recognize the value of HTTP Header SOAPAction"
path = '/uszip.asmx'
#http://www.webservicex.net/uszip.asmx?WSDL
# Create the SOAP Envelope
data = <<-EOF
<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>
<GetInfoByZIPResponse xmlns="http://www.webserviceX.NET">
<GetInfoByZIPResult>
<NewDataSet xmlns="">
<Table>
<CITY>Beverly Hills</CITY>
<STATE>CA</STATE>
<ZIP>90210</ZIP>
<AREA_CODE>310</AREA_CODE>
<TIME_ZONE>P</TIME_ZONE>
</Table>
</NewDataSet>
</GetInfoByZIPResult>
</GetInfoByZIPResponse>
</soap:Body>
</soap:Envelope>EOF
host = "www.webservicex.net"
http = Net::HTTP.new(host)
resp = http.post(path, data, { 'Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => 'GetInfoByZIP' })
Can anyone help me?
Thanks
The soapAction seems incorrect.
Try using the complete soapAction as specified in the example here:
http://www.webservicex.net/uszip.asmx?op=GetInfoByZIP
POST /uszip.asmx HTTP/1.1
Host: www.webservicex.net
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.webserviceX.NET/GetInfoByZIP"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetInfoByZIP xmlns="http://www.webserviceX.NET">
<USZip>string</USZip>
</GetInfoByZIP>
</soap:Body>
</soap:Envelope>
Change your code as follows:
resp = http.post(path, data, { 'Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => 'http://www.webserviceX.NET/GetInfoByZIP' })

Savon 2 returns nothing in Rails 4

Here is my Savon 2
client = Savon::Client.new(wsdl: "http://www.webservicex.net/uszip.asmx?WSDL")
client.operations
response = client.call(:get_info_by_zip, :message => { us_zip: "90210" })
response.to_hash
And response is:
{:get_info_by_zip_response=>{:#xmlns=>"http://www.webserviceX.NET"}}
In the SoapUI:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET">
<soapenv:Header/>
<soapenv:Body>
<web:GetInfoByZIP>
<!--Optional:-->
<web:USZip>90210</web:USZip>
</web:GetInfoByZIP>
</soapenv:Body>
</soapenv:Envelope>
I get this response:
<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>
<GetInfoByZIPResponse xmlns="http://www.webserviceX.NET">
<GetInfoByZIPResult>
<NewDataSet xmlns="">
<Table>
<CITY>Beverly Hills</CITY>
<STATE>CA</STATE>
<ZIP>90210</ZIP>
<AREA_CODE>310</AREA_CODE>
<TIME_ZONE>P</TIME_ZONE>
</Table>
</NewDataSet>
</GetInfoByZIPResult>
</GetInfoByZIPResponse>
</soap:Body>
</soap:Envelope>
For the life of me I cant figure it out. Can someone please have a look and let me know what am I doing wrong?
Thanks
Your tag within the message seems wrong, instead us_zip you should use "USZip" (in quotes!).
This works for me:
#!ruby
require 'savon'
require 'pp'
WSDL_URL = 'http://www.webservicex.net/uszip.asmx?wsdl'
client = Savon.client(
wsdl: WSDL_URL,
log: true, # set true to switch on logging
log_level: :debug,
pretty_print_xml: true
)
zip = ARGV[0] || "98052"
response = client.call(:get_info_by_zip,
message: { "USZip" => zip }
)
pp response.to_hash

Bing Translation Api access via SOAP interface from Ruby

I'm trying to use Bing Translator SOAP API (due to in HTTP API I'm getting 414 "Request too long" for not so big requests due to UTF-8 serialization).
So, I'm playing with bing_translator gem source trying to switch it from HTTP inerface to SOAP one using Savon SOAP toolkit.
My workflow as follows (access token getting function not shown):
WSDL_URI = 'http://api.microsofttranslator.com/V2/soap.svc?wsdl'
get_access_token
client = Savon.client(wsdl: WSDL_URI, headers: {'Authorization' => "Bearer #{#access_token['access_token']}"})
params = {
'from' => 'ru',
'to' => 'en',
'text' => 'Это текст для перевода',
'category' => 'general',
'contentType' => 'text/plain'
}
result = client.call(:translate, message: params)
Then SOAP request executes:
SOAP request: http://api.microsofttranslator.com/V2/soap.svc
Authorization: Bearer http%3a%2f%2fschemas.xmlsoap.org%2fws%2f2005%2f05%2fidentity%2fclaims%2fnameidentifier=invest_amurobl_ru&http%3a%2f%2fschemas.microsoft.com%2faccesscontrolservice%2f2010%2f07%2fclaims%2fidentityprovider=https%3a%2f%2fdatamarket.accesscontrol.windows.net%2f&Audience=http%3a%2f%2fapi.microsofttranslator.com&ExpiresOn=1381128612&Issuer=https%3a%2f%2fdatamarket.accesscontrol.windows.net%2f&HMACSHA256=Mw41PMMgw2n6ZVaGRXtwfR0vwMJUyIMltIyd9pa9MqA%3d
SOAPAction: "http://api.microsofttranslator.com/V2/LanguageService/Translate"
Content-Type: text/xml;charset=UTF-8
Content-Length: 454
<?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:Translate>
<to>en</to>
<text>Это текст для перевода</text>
<category>general</category>
<contentType>text/html</contentType>
<from>ru</from>
</wsdl:Translate>
</env:Body>
</env:Envelope>
And I'm getting error 500: Unhandled Service Exception
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">Unhandled Service Exception</faultstring>
<detail>
<int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</int>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
What's may be wrong? Can anyone who already using Bing Translator SOAP API to diff my soap-messages with yourself? Any advices to how to troubleshoot this.
Thanks for attention.
EDIT:
I've checked API with SoapUI, as #SteffenRoller advices and it works. Here is XML generated by SoapUI (values are inserted by hand):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v2="http://api.microsofttranslator.com/V2">
<soapenv:Header/>
<soapenv:Body>
<v2:Translate>
<!--Optional:-->
<v2:text>Текст, который я хочу перевести</v2:text>
<!--Optional:-->
<v2:from>ru</v2:from>
<!--Optional:-->
<v2:to>en</v2:to>
<!--Optional:-->
<v2:contentType>text/plain</v2:contentType>
<!--Optional:-->
<v2:category>general</v2:category>
</v2:Translate>
</soapenv:Body>
</soapenv:Envelope>
As you can see the only difference is that all the tags inside a <Body> are in the v2 namespace. In the XML, generated by Savon this namespace isn't present at all.
So, now question is: How to instruct Savon to use correct namespace for tags inside the message body?
Although, I think, this is a Savon bug, I'll file it to developers, as SoapUI have generated correct XML by the same WSDL, and Savon doesn't.
Okay. This is a known bug: Savon issue #340. It's related to composite WSDL files and won't bi fixed in current major release :-(
So, in our case we need to tell Savon, what is our namespace is, what is it's name (just to align it with WSDL) and prepend each tag's name with this namespace.
The correct code looks like this:
require 'savon'
access_token = "http%3a%2f%2fschemas.xmlsoap.org%2fws...CA9TEs%3d"
client = Savon.client(
wsdl: 'http://api.microsofttranslator.com/V2/soap.svc?wsdl',
namespace: 'http://api.microsofttranslator.com/V2',
namespace_identifier: :v2,
headers: {'Authorization' => "Bearer #{access_token}"},
)
params = {
'v2:text' => 'Это текст для перевода',
'v2:from' => 'ru',
'v2:to' => 'en',
'v2:contentType' => 'text/plain',
'v2:category' => 'general',
}
result = client.call(:translate, message: params)
puts result.body[:translate_response][:translate_result]
Any advices and corrections are welcome. Thanks.

Resources