View Savon Request XML without Sending to Server - ruby

I'm using the Savon gem to make a SOAP request using code similar to what's below. It's working, but I would like to view/capture the request XML without actually making a call to their server. I can view it now after a request is made by sticking a debugger line after the request and inspecting the client variable.
Does anyone know of a way to view the request XML without actually making a request? I want to be able to validate the XML against a schema using Cucumber or Rspec.
client = Savon::Client.new do |wsdl, http|
wsdl.document = "http://fakesite.org/fake.asmx?wsdl"
end
client.request(:testpostdata, :xmlns => "http://fakesite.org/") do
soap.header = { :cAuthentication => {"UserName" => "MyName", "Password" => "MyPassword" } }
soap.body = { :xml_data => to_xml }
end

Using Savon 2 I do it this way, write a method that return the request body from the client.
client = Savon::Client.new(....)
this is not mentioned in the documentation
def get_request
# list of operations can be found using client.operations
ops = client.operation(:action_name_here)
# build the body of the xml inside the message here
ops.build(message: { id: 42, name: "Test User", age: 20 }).to_s
end

You can directly via the Savon::Client#build_request method.
Example:
request = client.build_request(:some_operation, some_payload)
request.body # Get the request body
request.headers # Get the request headers
Take a peak # https://github.com/savonrb/savon/blob/master/lib/savon/request.rb for the full doc.

I am using Savon 2.11 and I can accomplish it with globals in the client:
def client
#client ||= Savon.client(soap_version: 2,
wsdl: config.wsdl,
logger: Rails.logger,
log: true)
end
More info on the globals here.
Then the logger spits out the host, the http verb and the complete xml ("headers" and body) for both request and response.

While I'm sure there's a better way to do this, I just overrode response.
class Savon::SOAP::Request
def response
pp self.request.headers
puts
puts self.request.body
exit
end
end

They've updated the API since the last post. Set this setting in Savon.client: :pretty_print_xml => true. After your call, search the logs for SOAP request:. The output is put to stdout. Check the console console history if you're testing your connection from the console.

Savon uses HTTPI to execute SOAP requests. HTTPI is a common interface on top of various Ruby HTTP clients. You could probably mock/stub the HTTP request executed by Savon via:
HTTPI.expects(:post).with do |http|
SchemaValidation.validate(:get_user, http.body)
end
Please note that I used Mocha for mocking the SOAP request, getting the HTTP body and validating it against some validation method (pseudo-code).
Currently, Savon does not support building up requests without executing them. So the only way to validate the request would be to intercept it.
If you would need Savon to support this feature, please let me know and open a ticket over at GitHub.
EDIT: There's also savon_spec, which is a little helper for basic fixture-based testing with Savon.

I had the same issue and patched Savon as follows:
module Savon
class Client
def get_request_xml operation_name, locals
Savon::Builder.new(operation_name, #wsdl, #globals, locals).pretty
end
end
end
This builds the XML and returns it as a string without sending it to the API endpoint. It doesn't accept a block argument in the same way client.call does, so it won't be able to reproduce every type of request you're making, but it meets my needs for now.

Related

Ruby/Sinatra - How can I call post in lambda class?

I'm make a little program in sinatra and I'm wanted to perfom some dynamic call of post, with diynamic uri, so I make a Connexion class like this:
class Connexion
def initialize(path)
#path = path
end
def sinatraPost
post "/#{#path}" do
# some code
end
end
end
But when I'm launch sinatraPost, I've got this error:
undefined method `post' for #<Connexion:0x000000026206b8> (NoMethodError)
How can I call the sinatra post method in my class ?
EDIT: Okay ! So, I change my strategy, I have this following code:
class Webhook < Sinatra::Base
get '/:name' do
# compare with names array
end
end
Webhook.run!
Thank's to everyone !
It looks like you're going about this the wrong way. If you want to set up your app to receive a POST request, you'll need routing logic in your controller. Sinatra controllers normally look like this:
require 'sinatra'
get '/route1' do
# do stuff
end
post '/route2' do
# do stuff
end
If you're using a modular app, you'll want to have your app inherit from Sinatra::Base. See the Sinatra docs for more.
Making a post request is different, and doesn't rely on Sinatra methods.
require 'net/http'
uri = URI("http://google.com")
headers = {}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
response = http.request(request)
Or something like that. Good luck!

get response of grape endpoint with def after

I'm using Grape. I want to define a method that runs after the response value has been calculated for a request, I tried following this:
http://www.sinatrarb.com/intro.html#Filters
and ended up with:
after do
puts response
end
however response is not defined. Apparently within this block, self refers to Grape::Endpoint, since after runs after the endpoint handler, I should be able to find the response value, right? I tried self.body however this returns nothing - it does, however, let me change the value of the response, but I want to retrieve the response value that was generated by my handler.
Ahh, so I solved this using rack middleware:
class CaptureResponse < Grape::Middleware::Base
def call!(env)
#env = env
#app_response = #app.call(#env)
body = #app_response[2]
body = body.body if body.kind_of? Rack::BodyProxy
puts body
#app_response
end
end
use CaptureResponse
I have no idea why just slapping in use CaptureResponse in config.ru works but it does!

Get only response headers in em_http_request

How can I get only response headers in an em_http_request?
I tried to use this code:
EventMachine.run do
http = EventMachine::HttpRequest.new('my_url').get
http.headers do |headers|
Fiber.current.resume headers
end
end
but I don't want to receive the whole body. How I can stop the request's execution?
http.close doesn't work.
UPD
http.instance_variable_get(:'#conn').close helps me, but may be you know more interesting solution
If you don't want the body, you should do a HEAD request instead of a GET. To terminate the event loop you need to explicitly call EventMachine.stop.
EventMachine.run do
http = EventMachine::HttpRequest.new('my_url').head
http.headers do |headers|
# do something with headers
EventMachine.stop
end
end

Thin post request not working

I have a Thin server that runs a Sinatra app. In one file, I send a POST request, but it never arrives to its route. The same code works if I run it with bundle exec ruby myapp.rb but when it's run by Thin using bundle exec thin start -p 3000, it simply does not match the post route.
post '/save/:website' do |website|
website_data = JSON.parse(request.body.read)
puts "Saving #{website} plugin data" #never reaches here
persister = Persister.new
persister.update_or_persist(website_data)
body "success"
end
And this is how I declare the POST request
def send_result (path, result)
request = Net::HTTP::Post.new('/save/something', initheader = {'Content-Type' => 'application/json'})
request.body = result.to_json
response = Net::HTTP.new('localhost', '3000').start { |http| http.request(request) }
end
Here is the config.ru
# config.ru
require "./myapp"
run Sinatra::Application
I never reach the puts in my post route, but I do reach it when it's deployed without thin. Why does this request not work?
Also, I had already opened another question about this, but this time I really need the Thin server because I will use Apache to proxy to Thin.

Mechanize and typhoeus

I am using Typhoeus with Hydra in order to make parallel requests . my end goal is to parse the typhoeus response into mechanize object.
url = "http://example.com/"
hydra = Typhoeus::Hydra.new
agent = Mechanize.new
request = Typhoeus::Request.new(url, :method => :get, :proxy => "#{proxy_host}:#{proxy_port}")
request.on_complete do |response| #Typhoeus::response object
body = response.body
uri = request.parsed_uri
page = agent.parse(uri, response, body)
end
hydra.queue(request)
hydra.run
the agent.parse method is giving me error because it cannot parse the typhoeus response object
/usr/local/rvm/gems/ruby-1.9.3-p194/gems/mechanize-2.5.1/lib/mechanize.rb:1165:in `parse': undefined method `[]' for #<Typhoeus::Response:0x00000012cd9da0> (NoMethodError)
Is there anyway i can convert Typhoeus response into Net::HTTPResponse object ?
Or is there any other way I can club Mechanize and Typhoeus together? So that, I can make parallel requests with typhoeus and scrape the data with Mechanize library.
I tried to create a Net::HTTPResponse(https://github.com/ruby/ruby/blob/trunk/lib/net/http/response.rb) from a Typhoeus::Response, but it didn't work out. Calling the initializer is easy, but setting the response body or headers not.
I looked into mechanize to see if it can be changed to use Typhoeus for making requests but I don't think thats possible right now. Net/http is really hard-wired into mechanize. I thought of a mechanize-typhoeus adapter, which would be nice.

Resources