I'm building a connection between REST-API and SOAP API in Ruby (without Rails).
For SOAP calls I use Savon gem, which is great.
However, I cannot figure out from the docs, how does Savon handle Timeout::Error?
Does it raise Savon::HTTPError or Savon::SOAPFault?
Please advise.
I was curious myself. After a bit of experimentation and skimming through Savon sources it seems that transport-level errors aren't handled and translated to Savon's own exception types, but thrown "as is", so if you need to handle them, you have to handle exceptions thrown by the underlying HTTP client library.
It's important to note that Savon supports multiple HTTP clients through the httpi abstraction layer. By default it just chooses one from those being available, but if you need to handle it's exceptions, you shouldn't rely on the automatic selection, but explicitly configure which HTTPI adapter should be used (e.g. HTTPI.adapter = :net_http).
The code below can be used to test the timeout scenario with HTTPI adapter of your choice.
Code for experimentation
Server (written in PHP, because there are no up-to-date working solutions for writing a dead-simple SOAP server like this, without a ton of boilerplate code, in Ruby):
<?php
// simple SOAP server - run with 'php -S localhost:12312 name_of_this_file.php'
class SleepySoapServer
{
public function hello()
{
sleep(3600); // take an hour's nap before responding
return 'Hello, world!';
}
}
$options = array('uri' => 'http://localhost:12312/');
$server = new SoapServer(null, $options);
$server->setClass(SleepySoapServer::class);
$server->handle();
Client (using Savon 2):
require 'savon'
HTTPI.adapter = :net_http # request Net::HTTP client from the standard library
uri = 'http://localhost:12312'
client = Savon.client do
endpoint uri
namespace uri
end
response = client.call :Hello
p response.body
If you don’t like to rescue errors, here’s how you can tell Savon not to raise them
Savon.configure do |config|
config.raise_errors = false
end
Related
I'm using Savon to make calls to a SOAP API. The API I'm accessing requires calls to be coming from a whitelisted IP address, so I'm using a QuotaGuard proxy.
The call that I'm making returns perfectly in IRB and also as a plain ruby script. When I put the exact same code into a method in my Rails model, the call times out because it isn't coming through the proxy IP. QuotaGuard has a dashboard where I can look at requests going through the proxy IP, so I know for sure that this call is not going through.
Here is my ruby script code:
require 'savon'
ping_request = Savon.client do
wsdl "http://xx.xxx.xxx.xx:8080/svbase4api/Ping?wsdl"
proxy "http://xxxxxxxxxxx:xxxxxxxxx#us-east-1-static-brooks.quotaguard.com:9293"
end
response = ping_request.call(:ping, message: {message: "oogly boogly"})
puts response.to_hash[:ping_response][:return]
The puts statement does exactly what I want. It puts "saved ping message oogly boogly"
Here's my Rails model:
class Debitcard < ActiveRecord::Base
def self.ping
ping_request = Savon.client do
wsdl "http://xx.xxx.xxx.xx:8080/svbase4api/Ping?wsdl"
proxy "http://xxxxxxxxxxx:xxxxxxxxx#us-east-1-static-brooks.quotaguard.com:9293"
end
response = ping_request.call(:ping, message: {message: "oogly boogly"})
puts response.to_hash[:ping_response][:return]
#ping_response = response.to_hash[:ping_response][:return]
end
end
And this is the result in the rails server when I press a button which posts to the controller action which calls the ping method:
D, [2014-10-23T18:38:08.587540 #2200] DEBUG -- : HTTPI GET request to
xx.xxx.xxx.xx (net_http) Completed 500 Internal Server Error in 75228ms
Errno::ETIMEDOUT (Operation timed out - connect(2)):
Can anyone shine a light on this? Thanks!
I want to call the method and get the response in my application from another application in Ruby on Rails technology, but here cross site scripting problem is there. so, i can i resolve this issue please help me it would be great.
http://video_tok.com/courses/get_course
def get_course
#course = Course.find(params[:id])
end
now i want to call this above method from this application which is running in edupdu.com domain
http://edupdu.com/call_course_method
def call_course_method
#course = redirect_to "http://video_tak.com/courses/get_course/1"
end
but it would be redirect into video_tak.com application.
i want to call get_course method and get #course object internally without redirect to another site.
Thanks in advance.
Cross-domain AJAX is indeed a problem, but none that could not be solved. In your get_course method you could return the course objects as a JSON response like so:
render json: #course
From there on you could either retrieve the course through JavaScript (AJAX), here you should use JSONP or inside Rails by issuing a HTTP GET request.
AJAX with JSONP
There is JSONP (JSON with padding), which is a communication technique for JavaScript programs to provide a method to request data from a server in a different domain. Look at
the documentation of jQuery.getJSON() and scroll down to the JSONP section.
If the URL includes the string "callback=?" (or similar, as defined by
the server-side API), the request is treated as JSONP instead. See the
discussion of the jsonp data type in $.ajax() for more details.
HTTP GET request
Simply use the Net::HTTP class:
require 'net/http'
require 'json'
url = URI.parse('http://video_tak.com/courses/get_course/1')
req = Net::HTTP::Get.new(url.to_s)
res = Net::HTTP.start(url.host, url.port) do |http|
http.request(req)
end
course_json = JSON.parse(res.body)
If you provide methods for your model to convert JSON into a domain object of yours, you can take it from there.
RPC
You can also use RPC to invoke methods between different Ruby processes, although I recommend this the least I do not want to omit it. There are several remote procedure call (RPC) libraries. The Ruby standard library provides DRb, but there are also implementations based on Ruby on Rails, for instance the rails-xmlrpc gem which allows you to implement RPC based on the XML-RPC protocol or the alternative protocol using JSON with json-rpcj
You will probably find even more libraries when searching for Rails RPC. From whatever library you pick, the concrete solution will differ.
Hi I'm using Savon to access some web services.
I'm using this code:
client=Savon.client(
wsdl: "WebService.wsdl",
env_namespace: "S",
convert_request_keys_to: :camelcase
)
response=client.call(:send_doc) do
message(
Attr1: "123",
Attr2: "ABC")
)
How do I get the request text sent to server?
Regards
Fak
Savon 2 provides an Observer interface. You can register an Observer which is notified before the request is send. The interface contains a Builder object where you find the XML content of the request. Builder#pretty() formats the XML content.
class Observer
def notify(_, builder, _, _)
puts builder.pretty
nil
end
end
Savon.observers << Observer.new
Alternativly you can add log: true to your client configuration. It enables the logging of the requests.
client = Savon.client(log: true, ...)
This isn't possible with stable versions of Savon. However, you can get the request using version 3 of Savon (see the Savon website for installation instructions and more detail). Example from the site:
client = Savon.new('http://example.com?wsdl')
operation = client.operation(service_name, port_name, operation_name)
operation.build # returns SOAP request
You could also monkeypatch Savon methods or set up a custom debugger to get this information with your current Savon version. See these StackOverflow answers for more information:
Accessing the request body of a SOAP request with Savon (Ruby on Rails)
View Savon Request XML without Sending to Server
I've written a basic REST API using sinatra.
Does anyone know the best way to write tests for it? I would like to do so using Ruby.
I've done my initial testing using curl. But I'd like to do something more robust. This is my first API - is there anything specific I should be testing?
The best way is a matter of opinion :) Personally, I like simple and clean. With tools like minitest, Watir and rest-client, you can put together a very simple test of both your REST interface as well as testing your web service through actual browsers (all major browsers are supported).
#!/usr/bin/ruby
#
# Requires that you have installed the following gem packages:
# json, minitest, watir, watir-webdrive, rest-client
# To use Chrome, you need to install chromedriver on your path
require 'rubygems'
require 'rest-client'
require 'json'
require 'pp'
require 'minitest/autorun'
require 'watir'
require 'watir-webdriver'
class TestReportSystem < MiniTest::Unit::TestCase
def setup
#browser = Watir::Browser.new :chrome # Defaults to firefox. Can do Safari and IE too.
# Log in here.....
end
def teardown
#browser.close
end
def test_report_lists # For minitest, the method names need to start with test
response = RestClient.get 'http://localhost:8080/reporter/reports/getReportList'
assert_equal response.code,200
parsed = JSON.parse response.to_str
assert_equal parsed.length, 3 # There are 3 reports available on the test server
end
def test_on_browser
#browser.goto 'http://localhost:8080/reporter/exampleReport/simple/genReport?month=Aug&year=2012'
assert(#browser.text.include?('Report for Aug 2012'))
end
end
Run the test cases by simply executing the script. There are many other testing systems and REST clients for Ruby which can be put to work in a similar way.
You might have a look at this approach http://anthonyeden.com/2013/07/10/testing-rest-apis-with-cucumber-and-rack.html
although many might say that using Cucumber is really more application or Acceptance testing and not unit testing, it does contain an approach to creating the HTTP headers and forming the http request, which I'm guessing might be where you are stuck?
Personally I don't have a problem with that since if you are truely going to unit test the API, you'd likely have to mock any units of code the api might be talking with (e.g. however you are persisting the data)
Seeing as I'm a QA guy not a dev, I'd be perfectly happy with using cucumber and testing it at that level, but I also greatly appreciate it when devs unit test, so while you might use rSpec instead of Cuke, perhaps the tip towards 'rack test' will be useful to what you are trying to accomplish.
You can try using airborne which is a framework written for just this purpose:
https://github.com/brooklynDev/airborne
You can test against either a live API, or against a Sinatra, Grape, Rails application.
I would use fakeweb gem to do unit testing with web services.
I would suggest client-api gem - it has loads of useful features specific to api automation which is easy to use and to maintain scripts.
https://github.com/prashanth-sams/client-api
Interestingly, this gem binds an api automation framework within itself. So, you don't even need a framework setup.
Key Features of client-api library:
Custom Header, URL, and Timeout support
URL query string customization
Datatype and key-pair value validation
Single key-pair response validation
Multi key-pair response validation
JSON response schema validation
JSON response content validation
JSON response size validation
JSON response is empty? validation
JSON response has specific key? validation
JSON response array-list sorting validation (descending, ascending)
Response headers validation
JSON template as body and schema
Support to store JSON responses of each tests for the current run
Logs support for debug
Custom logs remover
Auto-handle SSL for http(s) schemes
Example specs: https://github.com/prashanth-sams/client-api/tree/master/spec/client
Add this config snippet in the spec_helper.rb file:
ClientApi.configure do |config|
config.base_url = 'https://reqres.in'
config.headers = {'Content-Type' => 'application/json', 'Accept' => 'application/json'}
config.basic_auth = {'Username' => 'ahamilton#apigee.com', 'Password' => 'myp#ssw0rd'}
config.json_output = {'Dirname' => './output', 'Filename' => 'test'}
config.time_out = 10 # in secs
config.logger = {'Dirname' => './logs', 'Filename' => 'test', 'StoreFilesCount' => 2}
end
RSpec test scenarios look like,
api = ClientApi::Api.new
it "GET request" do
api.get('/api/users')
expect(api.status).to eq(200)
expect(api.message).to eq('OK')
end
it "POST request" do
api.post('/api/users', {"name": "prashanth sams"})
expect(api.status).to eq(201)
end
Note: This is an active project handling issues and new features based on user requirements
I need to write a soap client that is capable of sending and receiving soap messages.
This soap service does not have an associated WSDL file and soap4r and savon both seem to require one.
I have an example of what I need to do in Java, see the link below.
http://community.cecid.hku.hk/index.php/product/article/writing_hermes_2_ws_client_under_java/#ebms-2_0-sender-ws
I could use java for this, at this point it seems like it would be easier. However I personally prefer coding in ruby and our company has more ruby resources than java.
Can anyone confirm thats its possible to do something similar to java example in ruby without writing my own specialised soap library?. I need to be able to send a payload, which I believe is usually in the form of a soap attachment.
I am particularly interested in seeing soap4r examples that don't use a WSDL as I have had trouble finding any with google.
Any help much appreciated.
as of Savon v2, the syntax is slightly different
client = Savon.client do
endpoint "http://example.com"
namespace "http://v1.example.com"
end
http://savonrb.com/version2/client.html
Savon does not require a WSDL document. Please take a look at the new documentation. If you know the SOAP endpoint and target namespace, you can execute a SOAP request like this:
client = Savon::Client.new
wsdl.endpoint = "http://example.com"
wsdl.namespace = "http://soap.example.com"
end
client.request :any_soap_action do
soap.body = { :do => "something" }
end
client = Savon::Client.new
wsdl.endpoint = "http://example.com"
wsdl.namspace = "http://soap.example.com"
end
This does not work, it misses a the block name and the "e" in namespace:
client = Savon::Client.new do | wsdl |
wsdl.endpoint = "http://example.com"
wsdl.namespace = "http://soap.example.com"
end