Upload files using Faraday - ruby

I'm having issues attempting to upload a file to a web service using Faraday.
My code:
conn = Faraday.new('http://myapi') do |f|
f.request :multipart
end
payload = { :file => Faraday::UploadIO.new('...', 'image/jpeg') }
conn.post('/', payload)
Nothing seems to happen after attempting the post. When I inspect the response this is what I'm seeing:
#<Faraday::Response:0x007fd9e5903870 #env={:method=>:post, :body=>#<Faraday::CompositeReadIO:0x007fd9e5903a50 #parts=[#<Parts::FilePart:0x007fd9e5903e38 #head="-------------RubyMultipartPost\r\nContent-Disposition: form-data; name=\"file\"; filename=\"indymade-badge-horizontal.png\"\r\nContent-Length: 7821\r\nContent-Type: image/png\r\nContent-Transfer-Encoding: binary\r\n\r\n", #foot="\r\n", #length=8026, #io=#<CompositeReadIO:0x007fd9e5903b68 #ios=[#<StringIO:0x007fd9e5903bb8>, #<UploadIO:0x007fd9e514a3b8 #content_type="image/png", #original_filename="indymade-badge-horizontal.png", #local_path="/Users/anthonator/Downloads/indymade-badge-horizontal.png", #io=#<File:/Users/anthonator/Downloads/indymade-badge-horizontal.png>, #opts={}>, #<StringIO:0x007fd9e5903b90>], #index=0>>, #<Parts::EpiloguePart:0x007fd9e5903ac8 #part="-------------RubyMultipartPost--\r\n\r\n", #io=#<StringIO:0x007fd9e5903a78>>], #ios=[#<CompositeReadIO:0x007fd9e5903b68 #ios=[#<StringIO:0x007fd9e5903bb8>, #<UploadIO:0x007fd9e514a3b8 #content_type="image/png", #original_filename="indymade-badge-horizontal.png", #local_path="/Users/anthonator/Downloads/indymade-badge-horizontal.png", #io=#<File:/Users/anthonator/Downloads/indymade-badge-horizontal.png>, #opts={}>, #<StringIO:0x007fd9e5903b90>], #index=0>, #<StringIO:0x007fd9e5903a78>], #index=0>, :url=>#<URI::HTTPS:0x007fd9e5909d60 URL:https://myapi>, :request_headers=>{"User-Agent"=>"Faraday v0.8.7", "Content-Type"=>"multipart/form-data;boundary=-----------RubyMultipartPost", "Content-Length"=>"8062"}, :parallel_manager=>nil, :request=>{:proxy=>nil, :boundary=>"-----------RubyMultipartPost"}, :ssl=>{}, :response=>#<Faraday::Response:0x007fd9e5903870 ...>}, #on_complete_callbacks=[]>
irb(main):065:0> response.inspect
=> "#<Faraday::Response:0x007fd9e5903870 #env={:method=>:post, :body=>#<Faraday::CompositeReadIO:0x007fd9e5903a50 #parts=[#<Parts::FilePart:0x007fd9e5903e38 #head=\"-------------RubyMultipartPost\\r\\nContent-Disposition: form-data; name=\\\"file\\\"; filename=\\\"indymade-badge-horizontal.png\\\"\\r\\nContent-Length: 7821\\r\\nContent-Type: image/png\\r\\nContent-Transfer-Encoding: binary\\r\\n\\r\\n\", #foot=\"\\r\\n\", #length=8026, #io=#<CompositeReadIO:0x007fd9e5903b68 #ios=[#<StringIO:0x007fd9e5903bb8>, #<UploadIO:0x007fd9e514a3b8 #content_type=\"image/png\", #original_filename=\"indymade-badge-horizontal.png\", #local_path=\"/Users/anthonator/Downloads/indymade-badge-horizontal.png\", #io=#<File:/Users/anthonator/Downloads/indymade-badge-horizontal.png>, #opts={}>, #<StringIO:0x007fd9e5903b90>], #index=0>>, #<Parts::EpiloguePart:0x007fd9e5903ac8 #part=\"-------------RubyMultipartPost--\\r\\n\\r\\n\", #io=#<StringIO:0x007fd9e5903a78>>], #ios=[#<CompositeReadIO:0x007fd9e5903b68 #ios=[#<StringIO:0x007fd9e5903bb8>, #<UploadIO:0x007fd9e514a3b8 #content_type=\"image/png\", #original_filename=\"indymade-badge-horizontal.png\", #local_path=\"/Users/anthonator/Downloads/indymade-badge-horizontal.png\", #io=#<File:/Users/anthonator/Downloads/indymade-badge-horizontal.png>, #opts={}>, #<StringIO:0x007fd9e5903b90>], #index=0>, #<StringIO:0x007fd9e5903a78>], #index=0>, :url=>#<URI::HTTPS:0x007fd9e5909d60 URL:https://myapi>, :request_headers=>{\"User-Agent\"=>\"Faraday v0.8.7\", \"Content-Type\"=>\"multipart/form-data;boundary=-----------RubyMultipartPost\", \"Content-Length\"=>\"8062\"}, :parallel_manager=>nil, :request=>{:proxy=>nil, :boundary=>\"-----------RubyMultipartPost\"}, :ssl=>{}, :response=>#<Faraday::Response:0x007fd9e5903870 ...>}, #on_complete_callbacks=[]>"
The body is being set to a CompositeReadIO object and it seems to never be sending out a request.

It turns out that I needed to specify an adapter. Here's the code that ended up working.
conn = Faraday.new('http://myapi') do |f|
f.request :multipart
f.request :url_encoded
f.adapter :net_http # This is what ended up making it work
end
payload = { :file => Faraday::UploadIO.new('...', 'image/jpeg') }
conn.post('/', payload)

Something like this resolved my problem:
conn = Faraday.new(url: URL) do |faraday|
faraday.request :multipart #make sure this is set before url_encoded
faraday.request :url_encoded
faraday.adapter Faraday.default_adapter
end
file = Faraday::UploadIO.new(
params[:image].tempfile.path,
params[:image].content_type,
params[:image].original_filename
)
payload = { :file => file }
conn.post('/', payload)

Faraday after v0.16.0:
https://lostisland.github.io/faraday/middleware/multipart
"Multipart" middleware detects files and encodes with "multipart/form-data":
payload[:profile_pic] = Faraday::FilePart.new('/path/to/avatar.jpg', 'image/jpeg')
conn.put '/profile', payload

Related

Ruby Open-URI - specify content type?

I'm trying
io = open(uri, {ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER})
the result is not what I want.
io.content_type
=> "text/html"
Can I specify I want "application/pdf"?
This seems to work:
io = open(uri, "Content-Type" => "application/pdf", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)

Mocking methods in Puppet rspec tests

I've implemented a custom Puppet function that queries a Keystone server for information. The module that defines this function includes some helper methods that perform the actual work of querying keystone. Broadly, the structure looks like this:
def authenticate(auth_url, username, password)
...
end
def list_tenants(auth_url, token)
...
end
module Puppet::Parser::Functions
newfunction(:lookup_tenant, :type => :rvalue) do |args|
...
end
end
I would like to mock out the authenticate and list_tenants methods
during testing so that I can test the rest of the Puppet module in the
absence of an actual Keystone server.
I haven't previously worked with either Ruby or Rpsec before, and I'm
having a hard time finding examples of how to provide stubs for these
internal methods.
So far I have a stub rspec file that simply verified the existence of
the function:
require 'spec_helper'
describe 'lookup_tenant' do
it "should exist" do
Puppet::Parser::Functions.function("lookup_tenant").should == "function_lookup_tenant"
end
# This will fail because there is no keystone server.
it "should fail" do
should run.with_params(
'http://127.0.0.1:35357/v2.0',
'admin_user',
'admin_password',
'admin_tenant_name',
'target_tenant_name'
).and_raise_error(KeystoneError)
end
end
I would like to be able to provide custom returns from the
authenticate and list_tenants methods (or even raise exceptions
from inside these methods) so that I can test the behavior of the
lookup_tenant function in different failure scenarios.
WebMock could be used for simulating the http requests as stubs. Here is the link to the github repo: https://github.com/bblimke/webmock
For folks who haven't seen webmock before, I wanted to leave some information here about why it's particularly awesome.
So, I have in my module some code that makes an http request:
url = URI.parse("#{auth_url}/tokens")
req = Net::HTTP::Post.new url.path
req['content-type'] = 'application/json'
req.body = JSON.generate(post_args)
begin
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
if res.code != '200'
raise KeystoneError, "Failed to authenticate to Keystone server at #{auth_url} as user #{username}."
end
rescue Errno::ECONNREFUSED
raise KeystoneError, "Failed to connect to Keystone server at #{auth_url}."
end
By simply adding a require to the start of the spec file:
require `webmock`
Attempts to open a connection will result in:
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: POST http://127.0.0.1:35357/v2.0/tokens with body '{"auth":{"passwordCredentials":{"username":"admin_user","password":"admin_password"},"tenantName":"admin_tenant"}}' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
with(:body => "{\"auth\":{\"passwordCredentials\":{\"username\":\"admin_user\",\"password\":\"admin_password\"},\"tenantName\":\"admin_tenant\"}}",
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
And that's just about all the information you need to stub out the
call. You can make the stubs as granular as necessary; I ended up
using something like:
good_auth_request = {
'auth' => {
'passwordCredentials' => {
'username' => 'admin_user',
'password' => 'admin_password',
},
'tenantName' => 'admin_tenant',
}
}
auth_response = {
'access' => {
'token' => {
'id' => 'TOKEN',
}
}
}
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
with(:body => good_auth_request.to_json).
to_return(:status => 200, :body => auth_response.to_json, :headers => {})
And now I can test my module when there is no Keystone server
available.

Force rebuild the SOAP-Header after authenticating

My SOAP-Server expects every request to have a valid token in the soap-header to authenticate the soap-client. This token is only valid for a certain period of time, so I have to expect it to be invalid in every call.
I am trying to find a way to force savon to rebuild the SOAP-Header (i.e. use the new auth-token) after I (re)authenticate with the SOAP-Server. I am not sure, if that is either a savon problem or a ruby one. Here is what I have so far.
class Soapservice
extend Savon::Model
# load stored auth-token
##header_data = YAML.load_file "settings.yaml"
client wsdl: 'locally-cached-wsdl.xml',
soap_header: {'verifyingToken' => ##header_data}
operations :get_authentification_token, :get_server_time
# request a new auth-token and store it
def get_authentification_token
response = super(:message => {
'oLogin' => {
'Username' => 'username',
'Userpass' => 'password'
}
})
settings = {
'UserID' => response[:user_id].to_i,
'Token' => response[:token],
}
File.open("settings.yaml", "w") do |file|
file.write settings.to_yaml
end
##header_data = settings
end
def get_server_time
return super()
rescue Savon::SOAPFault => error
fault_code = error.to_hash[:fault][:faultstring]
if fault_code == 'Unauthorized Request - Invalide Token'
get_authentification_token
retry
end
end
end
When I call
webservice = Soapservice.new
webservice.get_server_time
with an invalid Token, it reauthenticates and saves the new Token successfully, but the retry doesn't load the new header (the result is an infinite loop). Any ideas?
I added rubiii's answer from the GitHub-Issue here for future reference:
class Soapservice
# load stored auth-token
##header_data = YAML.load_file "settings.yaml"
def initialize
#client = Savon.client(wsdl: 'locally-cached-wsdl.xml')
end
def call(operation_name, locals = {})
#client.globals[:soap_header] = {'verifyingToken' => ##header_data}
#client.call(operation_name, locals)
end
# request a new auth-token and store it
def get_authentification_token
message = {
'Username' => 'username',
'Userpass' => 'password'
}
response = call(:get_authentification_token, :message => message)
settings = {
'UserID' => response[:user_id].to_i,
'Token' => response[:token],
}
File.open("settings.yaml", "w") do |file|
file.write settings.to_yaml
end
##header_data = settings
end
def get_server_time
call(:get_server_time)
rescue Savon::SOAPFault => error
fault_code = error.to_hash[:fault][:faultstring]
if fault_code == 'Unauthorized Request - Invalide Token'
get_authentification_token
retry
end
end
end
rubiii added:
notice that i removed Savon::Model, as you actually don't need it and i don't know if it supports this workaround.
if you look at the #call method, it accesses and changes the globals before every request.

Connect Rails 3 to Salesforce/Any other App via OAuth

Has anybody connected to Salesforce through Rails 3 App via oauth? Could you please post code for doing same. I am trying to same but I get some error below is my code
def oauth_client
consumer_key = '....'
consumer_secret = '....'
oauth_options = {
:site => 'https://login.salesforce.com',
:scheme => :body,
:request_token_path => '/_nc_external/system/security/oauth/RequestTokenHandler',
:authorize_path => '/setup/secur/RemoteAccessAuthorizationPage.apexp',
:access_token_path => '/_nc_external/system/security/oauth/AccessTokenHandler',
}
OAuth::Consumer.new consumer_key, consumer_secret, oauth_options
end
def oauth_redirect_uri
uri = URI.parse(request.url)
uri.path = '/sfdc/oauth_callback'
uri.query = nil
# uri = "http://localhost:3000/sfdc/oauth_callback"
uri.to_s
end
def oauth_connect
consumer_key = '...' # from SalesForce
consumer = oauth_client
request_t = consumer.get_request_token
redirect_to request_t.authorize_url(
:redirect_uri => oauth_redirect_uri,
:oauth_consumer_key => consumer_key
)
end
def oauth_callback
access = request_t.get_access_token :oauth_verifier => params[:oauth_verifier]
p access
render :text => access.token
end
Error undefined method get_access_token for #<ActionDispatch::Request:0x12b79f370>. the request variable is nil here. How do I get it back?
The rforce gem has quite a bit of an example that I pasted below. However you might just want to use rforce instead of rolling your own.
def init_server(url)
#url = URI.parse(url)
if (#oauth)
consumer = OAuth::Consumer.new \
#oauth[:consumer_key],
#oauth[:consumer_secret],
{
:site => url,
:proxy => #proxy
}
consumer.http.set_debug_output $stderr if show_debug
#server = OAuth::AccessToken.new \
consumer,
#oauth[:access_token],
#oauth[:access_secret]
class << #server
alias_method :post2, :post
end
else
#server = Net::HTTP.Proxy(#proxy).new(#url.host, #url.port)
#server.use_ssl = #url.scheme == 'https'
#server.verify_mode = OpenSSL::SSL::VERIFY_NONE
# run ruby with -d or env variable SHOWSOAP=true to see SOAP wiredumps.
#server.set_debug_output $stderr if show_debug
end
end

Parse WSDL file with SOAP4R

Is there any example of WSDL Parser using SOAP4R? I'm trying to list all operations of WSDL file but I can't figure it out :( Can you post me some tutorial?
Thx
Maybe that isn't answer you want, but I recommend you switch to Savon. For example, your task looks like this snippet (this example taken from github's savon page):
require "savon"
# create a client for your SOAP service
client = Savon::Client.new("http://service.example.com?wsdl")
client.wsdl.soap_actions
# => [:create_user, :get_user, :get_all_users]
# execute a SOAP request to call the "getUser" action
response = client.request(:get_user) do
soap.body = { :id => 1 }
end
response.body
# => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }

Resources