Use cached data by FaradayMiddleware::Caching - ruby

I use Faraday for request api. I want to cache response body and use it instead of request for specifed expired time.
I found FaradayMiddleware::Caching and I can cache response body, but I do not know how use cache instead of request.
API service which I use do not support http cache, so I can not use faraday-http-cache gem.
Below my code
connection = Faraday.new(url: 'http://exapmle.com') do |faraday|
faraday.request :url_encoded
cache_dir = File.join('/tmp/cache')
faraday.response :caching do
ActiveSupport::Cache::FileStore.new cache_dir, :expires_in => 3600
end
faraday.adapter Faraday.default_adapter
end
connection.get '/path/to/api', version: nil
How can I use cached data instead of normal response?
Cache key is request url, but url is dynamically change. So I think I need a way to get url which constructed by Faraday.

Related

Failed authentication with Ruby HTTPClient

I have this very simple Ruby code which makes Ruby POST request.
For some reason it creates 2 requests - one authentication error and one successful request:
def execute
request = HTTPClient.new()
request.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
request.set_auth('https://test.net/api', 'user', 'pass')
request_body = File.read("requests/xml/req.xml")
response = request.post('https://test.net/api', request_body, {'Content-Type' => 'application/xml', 'cache-control' => 'no-cache'}).body
puts 'called'
end
I suppose that some configuration if missing for HTTPClient?
Can you advise how I can fix this?
This is an expected behavior. The client won't send the credentials unless they are required. When the client gets a 401 status code then it sends the request again but now with the credentials.
You can force some clients to send the credentials on the first request.
Ruby's HTTPClient has a force_basic_auth that does that.

Mocking a post request with binary data in Sinatra

I have an endpoint in my Sinatra application that will be receiving binary data as part of the body. The other application sending it data will have a Faraday request that looks like this:
connection = Faraday.new(url: "https://example.com/post_data") do |conn|
conn.request :multipart
conn.adapter :net_http
conn.headers['Content-Type'] = 'octet/stream'
end
#response ||= connection.post do |req|
req.params = { :host => host,
:verification => "false"}
req.body = my_base64_encoded_binary
end
Then, in Sinatra, I will have an endpoint that receives those request parameters and binary data and passes it along to a model, like so:
post '/post_data' do
request.body.rewind
payload = request.body.read
raise Sinatra::NotFound unless payload and params[:host]
output = MyOutput.new(params, payload)
content_type 'application/json'
body output.data
end
When I try to test this endpoint using Rack::Test helpers, I end up in a weird situation. I can't seem to create the proper mock in order to test this endpoint properly. Based on some manual testing with PostMan, I'm pretty certain my endpoint works properly. It's just the test that's holding me up. Here is the spec:
it "should return a json response" do
post(url, :host => host, :verification => "false") do |req|
req.body = [my_binary]
req.content_type = "application/octet-stream"
end
expect(last_response.status).to eq(200)
expect(last_response.content_type).to eq("application/json")
end
And when I inspect what the incoming request looks like in the test, it does not contain a proper body. params is properly set to the host and verification settings I set, but the body is also being set to the params (inspected through payload = request.body.read) instead of the binary.
Is there a different way to set up the mock so that the binary properly is set to the body of the request, and the parameters are still set to the request parameters properly?
The answer is that the only way to post the body is where I was adding the params in the rack test helper. I needed to take the params and move them into the query string of the URL in the request, and then only add the binary as the body of the post request in the test helper.
let(:url) { "http://example.com/post_data?host=>#{host}&verification=#{verification}" }
it "should return a json response" do
post(url, my_binary)
expect(last_response.status).to eq(200)
expect(last_response.content_type).to eq("application/json")
end

Capture HTTP call response using Chef

I'm currently using Chef to build out a cookbook that has to fire off a bunch of POST calls to this API and I have to capture the response in a variable to use it in a second HTTP call.
I've tried using the http_request resource from Ruby but I can only fire the call but don't know how to get the response captured:
http_request 'authorize' do
action :post
url '*****************************' headers ({
'Content-Type' => 'application/json'
}) message ({
:Username => "**********",
:Password => "**********"
}).to_json
end
In another attempt, I tried using Chef's http client to fire off a POST call and a get a response:
require "net/https"
require "uri"
require "json"
uri = URI("******************************")
req = Net::HTTP::Post.new(uri)
req.set_form_data("Username" => "********", "Password" => "*********")
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
case res
when Net::HTTPSuccess, Net::HTTPRedirection
# OK
else
res.value
end
But I keep getting this error when I run the chef-client on my node:
EOFError
--------
end of file reached
How can I send off a POST call using Chef/Ruby and capture its response?
You want to use the Chef::HTTP client class, see https://coderanger.net/chef-tips/#4 for an example.
In my case the request url had https so I had to add this line and it was working.
http.use_ssl = true
If you want a more generic approach that can handle both http and https see this answer -
https://stackoverflow.com/a/9227933/6226283

Faraday Connection: Switching the request mode?

I am using faraday to handle some requests to an internal API. The API endpoints use a CSRF token, so I am also using faraday-cookie_jar.
For a couple of the API endpoints, they require a :multipart request. Others do not.
Question
Is there any way to utilize the same Connection object, but switch whether or not you are doing a :multipart or a :url_encoded request?
Currently, I have to use two connections depending on which type of request I'm making. It does not seem you can change a connection's request method after it has done at least 1 request.
#connection = Faraday.new(url: 'http://localhost:3000') do |faraday|
faraday.request :url_encoded
faraday.use :cookie_jar
faraday.adapter Faraday.default_adapter
end
#connection.get '/something'
# try to change to :multipart
#connection.request :multipart # => Faraday::RackBuilder::StackLocked: can't modify middleware stack after making a request
It doesn't seem to allow you to switch after you've made a request. I know that you can modify the request a bit for each request itself by passing a block, but I can't seem to find where to modify to switch to :multipart in it.
Thanks in advance.
You'll want to include both middleware options when creating your connection (:url_encoded and :multipart), and then switch using explicit Content-Type headers.
Per the Faraday ReadMe, you can specify 'em both. An excerpt:
Faraday.new(...) do |conn|
# POST/PUT params encoders:
conn.request :multipart
conn.request :url_encoded
conn.adapter :net_http
end
This request middleware setup affects POST/PUT requests in the following way:
Request::Multipart checks for files in the payload, otherwise leaves everything untouched;
Request::UrlEncoded encodes as "application/x-www-form-urlencoded" if not already encoded or of another type.
So adding :multipart still allows for url-encoded posts, because it only matters when there are files in the payload. Then, if you explicitly set your Content-Type for the file upload, you should be fine - Faraday will use the correct request middleware because you explicitly told it to use multipart. But if you don't specify, it will default to url-encoded.
# works using :url_encoded
#connection.post '/something' do |req|
req.body = { some: 'posted fields' }
end
# works using :multipart because Content-Type was explicitly set
#connection.post '/some_file_endpoint' do |req|
req.headers['Content-Type'] = 'multipart/form-data'
req.body = { file_field: Faraday::UploadIO.new(file_path, 'text/plain') }
end
Calling #connection.request :multipart adds Faraday::Request::Multipart to #connecton.builder.handlers. If you want to remove something, you can manipulate that array.
I make no claim that messing with Faraday's (relative) internals is a good idea. Keeping your two connections sounds like a better plan.
Sean’s answer sounds like the right thing to do here, but if you do want to modify your connection, you have to duplicate it before:
#connection = #connection.dup
#connection.request :multipart
This can also be used to delete middleware from a connection:
#connection = #connection.dup
#connection.builder.delete(Faraday::Request::lookup_middleware(:multipart))

Can't generate an OAuth1 Authorization header using Typhoeus in Ruby

I'm trying to send a request via Typhoeus to Bitbucket's API using Ruby, and I'm doing so with credentials from the "OAuth consumers" page - the consumer_key and consumer_secret. I can get the token just fine, but I get an error when trying to generate the header from the OAuth helper. Here's the relevant code (note that this is not Rails; I'm using Sinatra):
# oauth_token and oauth_token_secret are from a request to https://bitbucket.org/api/1.0/oauth/request_token
#consumer = OAuth::Consumer.new(
consumer_key,
consumer_secret,
)
#token = OAuth::Token.new(oauth_token, oauth_token_secret)
options = {
method: :post,
body: body.to_json
}
oauth_params = {:consumer => #consumer, :token => #token}
hydra = Typhoeus::Hydra.new
url = "https://api.bitbucket.org/2.0/repositories/..."
req = Typhoeus::Request.new(url, options)
oauth_helper = OAuth::Client::Helper.new(req, oauth_params.merge(:request_uri => url))
req.options[:headers].merge!({"Authorization" => oauth_helper.header})
The last line fails with a OAuth::RequestProxy::UnknownRequestType error. Basically what it's saying is that the OAuth RequestProxy doesn't understand the Typhoeus::Request object; from what I can see, it only has support for Net::HTTPGenericRequest and Hash. The Typhoeus documentation indicates that this should work, so it's possible I'm doing something wrong.
Alternatively, is there a better way to do this? Should I use a different request library? HTTParty doesn't have great support for auth headers like this. Thanks!
You need to explicitly require the RequestProxy.
require 'typhoeus'
require 'oauth'
require 'oauth/request_proxy/typhoeus_request'

Resources