invalid SOAP request needs - ruby-on-rails

I'm using savon to make requests against a SOAP service.
Regardless, I'm having trouble making Savon client call.
below mentioned are my ruby code
client = Savon.client(
wsdl: "https://<URL>",
soap_header: { 'Content-Type:' => "text/xml"},
log: true,
pretty_print_xml: true
)
client.call(:get_active_employees_info,:xmlns =>"http://tempuri.org/") do
message("AppID" => "*******","Username" => "*****","Password" => "******")
end
which return
Savon::HTTPError: HTTP error (400)
but same snippets run on Postman
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetActiveEmployeesInfo xmlns="http://tempuri.org/">
<AppID>**********</AppID>
<Username>********</Username>
<Password>**********</Password>
</GetActiveEmployeesInfo>
</soap12:Body>
</soap12:Envelope>

I think that the problem is related to the request body that you are building.
Since you have a working example, I would start by checking the request's body build by savon. To do this, use the following code:
ops = client.operation(:get_active_employees_info)
puts ops.build(message: {"AppID" => "*******","Username" => "*****","Password" => "******"}).pretty
Then, compare generated message with the one from Postman. I hope it helps.

Related

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.

Using Savon and nokogiri to make a Soap Request

A sample request that I am trying to make looks like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetList xmlns="http://tempuri.org/">
<listRequest i:type="b:NpsListRequest" xmlns:a="http://schemas.datacontract.org/2004/07/Services.List" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:b="http://schemas.datacontract.org/2004/07/Services.List.Strategies">
<a:id>1</a:id>
</listRequest>
</GetList>
</s:Body>
</s:Envelope>
I can replicate that exactly, using Nokogiri, like so:
builder = Nokogiri::XML::Builder.new do |xml|
xml['s'].Envelope('xmlns:s' => "http://schemas.xmlsoap.org/soap/envelope/") {
xml['s'].Body {
xml.GetList('xmlns' => "http://tempuri.org/") {
xml.listRequest('i:type' => "b:NpsListRequest", 'xmlns:a' => "http://schemas.datacontract.org/2004/07/Services.List", 'xmlns:i' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:b' => "http://schemas.datacontract.org/2004/07/Services.List.Strategies") {
xml['a'].id(1)
}
}
}
}
end
builder.to_xml
Then, I build my soap_client using Savon:
def soap_client
soap_client = Savon.client(
wsdl: "http://1.1.1.1/Api.svc?wsdl", #PLE
headers: {"Authorization" => "Basic"},
basic_auth: ['user', 'password'],
env_namespace: :soapenv,
namespace_identifier: :ser,
ssl_verify_mode: :none,
log: true,
:pretty_print_xml => true
)
end
Now, I need to make a :get_list call that looks like the above sample request.
soap_client.operations returns this:
[:get_list] So I know I'm authenticated and the client is working correctly.
I can't figure out how to actually make the :get_list call.
I would like something simple where I don't even have to use Nokogiri to build my own xml, like this:
soap_client.call :get_list, message: {id => 1}
But that doesn't seem to be a feasible solution. Any thoughts on how I can pass the XML I'm building using Nokogiri through to the :get_list call for my soap_client? I am open to other suggestions as well.

Need to remove SOAP Action from Savon generated request

I need to remove the SOAPAction from my request using Savon 2.
This is how I build the client:
client = Savon.client do
wsdl "http://servername:port/PingService?wsdl"
convert_request_keys_to :none
env_namespace :soapenv
namespaces({
'xmlns:pin' => 'http://servername:port/pingService_v1'
})
end
I use the following call to make the request:
client.call(:invoke, message: { "pin:PingReq" => { "pin:PingDB" => "true", "pin:PingG" => "true" }})
This is the request that is sent:
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://support.cxf.transport.mule.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pin="http://servername/pingService_v1">
<soapenv:Body>
<tns:invoke>
<pin:PingReq>
<pin:PingDB>true</pin:PingDB>
<pin:PingG>true</pin:PingG>
</pin:PingReq>
</tns:invoke>
</soapenv:Body>
</soapenv:Envelope>
A valid request would have everything except for the tns:invoke tags.
With Savon 2.x you can roll your own body by doing this:
client.call(:method, xml: "<tns:invoke><pin:PingReq> ...")

Enforcing XML structure order in Savon

I'm currently trying to build a provisioning service of a kind for a local instance of Open-Xchange, using Rails 2.3.8, Savon and OX's SOAP API.
Via the console, I can issue the following commands that will work;
>> client = Savon::client("http://192.168.2.195/servlet/axis2/services/OXContextService?wsdl")
=> #<Savon::Client:0x2b5151bad790 #wsdl=#<Savon::Wasabi::Document:0x2b5151bad678 #document="http://192.168.2.195/servlet/axis2//services/OXContextService?wsdl", #request=#<HTTPI::Request:0x2b5151bad628>>, #http=#<HTTPI::Request:0x2b5151bad628>, #config=#<struct Savon::Config _logger=#<Savon::Logger:0x2b5151bad6c8 #device=#<IO:0x2b514a775ad0>>, pretty_print_xml=nil, raise_errors=true, soap_version=1, env_namespace=nil, soap_header=nil>>
>> client.request :list_by_database do
?> soap.body = {
?> :auth => {
?> :login => "oxadminmaster",
?> :password => "admin_master_password"
>> },
?> :db => {
?> :id => 3
>> }
>> }
>> end
HTTPI executes HTTP GET using the net_http adapter
SOAP request: https://192.168.2.195/servlet/axis2/services/OXContextService.OXContextServiceHttpsSoap11Endpoint/
SOAPAction: "urn:listByDatabase", Content-Type: text/xml;charset=UTF-8, Content-Length: 657
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ins0="http://soap.admin.openexchange.com" xmlns:ns="http://soap.admin.openexchange.com" xmlns:ins1="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:ins2="http://dataobjects.rmi.admin.openexchange.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><ins0:listByDatabase><ins0:db><ins1:id>3</ins1:id></ins0:db><ins0:auth><ins2:login>oxadminmaster</ins2:login><ins2:password>admin_master_password</ins2:password></ins0:auth></ins0:listByDatabase></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
warning: peer certificate won't be verified in this SSL session
SOAP response (status 200):
It works, no problems there. However, when I try the following code...
class Ox_Context_Service
extend Savon::Model
attr_accessor :data, :returnCode
document "http://192.168.2.195/servlet/axis2/services/OXContextService?wsdl"
def list_by_database oxMasterUser, oxMasterPassword
begin
response = client.request :list_by_database do
soap.body = {
:auth => {
#for the sake of testing
:login => "#{oxMasterUser}",
:password => "#{oxMasterPassword}"
},
:db => {
:id => 3
#For the sake of testing
}
}
end
if response.success?
data = response.body[:list_by_database_response][:return]
if data
#data = data
#returnCode = "#{response.http.code}"
end
end
end
rescue Savon::Error => fault
#data = {}
#returnCode = "#{fault}"
end
end
Using the following CURL...
curl localhost:4545/oxContextService/list_by_database -d 'oxUsername=oxadminmaster' -d 'oxPassword=admin_master_password'
I get...
HTTPI executes HTTP GET using the net_http adapter
SOAP request: https://192.168.2.195/servlet/axis2/services/OXContextService.OXContextServiceHttpsSoap11Endpoint/
SOAPAction: "urn:listByDatabase", Content-Type: text/xml;charset=UTF-8, Content-Length: 657
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ins0="http://soap.admin.openexchange.com" xmlns:ns="http://soap.admin.openexchange.com" xmlns:ins1="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:ins2="http://dataobjects.rmi.admin.openexchange.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><ins0:listByDatabase><ins0:auth><ins2:login>oxadminmaster</ins2:login><ins2:password>admin_master_password</ins2:password></ins0:auth><ins0:db><ins1:id>3</ins1:id></ins0:db></ins0:listByDatabase></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
warning: peer certificate won't be verified in this SSL session
SOAP response (status 500):
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><soapenv:Fault><faultcode>soapenv:Server</faultcode><faultstring>Authentication failed</faultstring><detail /></soapenv:Fault></soapenv:Body></soapenv:Envelope>
So- two identical calls, two different results. What could be going wrong here, SO?
Edit: The Rails call fails because the Auth block is sent first, instead of last. I think the new question is how to enforce XML structure order without explicitly writing out the XML.
soap.body = {
:auth => {
#for the sake of testing
:login => "#{oxMasterUser}",
:password => "#{oxMasterPassword}"
},
:db => {
:id => 3
#For the sake of testing
}
:order! => [:db, :auth]
}
The order! will force a specific order based on that array. Lesson learned: Read the manual.

eBay API GetCategoryFeaturesRequest returns 'Unsupported API call' error

I am trying to get CategoryFeatures using Typhoeus::Request from eBay API. I am using Ruby Version 1.9.3 and Rails 3.2.3 with Ubuntu Server 11.04.
My code is below:
xml = <GetCategoryFeaturesRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<RequesterCredentials>
<eBayAuthToken> MY_eBayAuthToken </eBayAuthToken>
</RequesterCredentials>
<WarningLevel>High</WarningLevel>
</GetCategoryFeaturesRequest>
header = {
"X-EBAY-API-DEV-NAME" => my_devID,
"X-EBAY-API-APP-NAME" => my_appID,
"X-EBAY-API-CERT-NAME" => my_certID,
"X-EBAY-API-CALL-NAME" => "GetCategoryFeatures",
"X-EBAY-API-COMPATIBILITY-LEVEL" => "1.3.0",
"X-EBAY-API-SITEID" => 1,
"Content-Type" => "text/xml",
}
url = "https://api.sandbox.ebay.com/ws/api.dll "
res = Typhoeus::Request.post(url, :body => xml, :headers => header )
I got this error:
<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
<GeteBayOfficialTimeResponse xmlns=\"urn:ebay:apis:eBLBaseComponents\">
<Timestamp>2012-07-05 06:41:28</Timestamp>
<Ack>Failure</Ack><Errors>
<ShortMessage>Unsupported API call.</ShortMessage>
<LongMessage>The API call \"GeteBayOfficialTime\" is invalid or not supported in this release.</LongMessage>
<ErrorCode>2</ErrorCode>
<SeverityCode>Error</SeverityCode>
<ErrorClassification>RequestError</ErrorClassification>
</Errors>
<Build>14991382</Build>
</GeteBayOfficialTimeResponse>
Does anyone know how to fix it?
Try to test your XMl in the test tool :
https://developer.ebay.com/DevZone/build-test/test-tool/default.aspx
I run this XML and it works:
<?xml version="1.0" encoding="utf-8"?>
<GetCategoryFeaturesRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<RequesterCredentials>
<eBayAuthToken> my_Token </eBayAuthToken>
</RequesterCredentials>
</GetCategoryFeaturesRequest>

Resources