Using webmock to stub partial headers - ruby

I am creating tests using webmock. I want to test that a particular header field is set, but I don't care about other header fields. When I use this:
stub_request(:get, "https://myhost.com/api").
with(:headers => {:user_agent => 'Custom user agent'}).
to_return(:status => 200, :body => '')
I get an error because I am not stubbing all the headers:
Unregistered request: GET https://myhost.com/api with headers {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Custom user agent'}
You can stub this request with the following snippet:
stub_request(:get, "https://myhost.com/api").
with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Custom user agent'}).
to_return(:status => 200, :body => "", :headers => {})
I don't care about the Accept and Accept-Encoding headers - how do I stub so they are ignored?

Web mock does partial matching by default. In your case, the :user_agent key is a symbol which won't match the string 'User-Agent'. Converting to a string should work:
'User-Agent'=>'Custom user agent'

You can use hash_including:
stub_request(:get, "https://myhost.com/api").
with(:headers => hash_including({:user_agent => 'Custom user agent'})).
to_return(:status => 200, :body => '')

Related

How to pass freerange anything inside a block in a Mocha Expectation (Ruby)

I'm trying to validate an expectation of a method that has a block being passed to it. Everything works perfectly fine as long as I don't use the anything keyword, however I need to use the anything keyword so as not to match all parts of the expectation.
Actual Function Call to be tested.
RestClient::Request.execute(:method => :post, :url => some_url, :headers => {}.to_json, :payload => {}.to_json)
And when I'm testing it out,
These work (note the usage of anything)
RestClient::Request.expects(:execute).with(:method => :post, :url => some_url, :headers => {}.to_json, :payload => {}.to_json)
RestClient::Request.stubs(:execute).with(:method => :post, :url => some_url, :headers => {}.to_json, :payload => {}.to_json)
RestClient::Request.stubs(:execute).with(anything)
This doesn't (note the usage of anything)
RestClient::Request.stubs(:execute).with(:method => :post, :url => some_url, :headers => anything, :payload => anything)
My problem is that I only want to validate the URL and the method but not the headers and the body of the request. What am I missing ?
Figured it out - In case it helps someone, this is how it is done
Use the .with(has_entries( ... )) method and pass only the params that are to be validated - avoid passing anything to the .with method.
RestClient::Request.expects(:execute).with(has_entries(:method => :post, :url => url))

Can't get access token for ExactOnline with OAuth

I'm following the OAuth tutorial here to get a access code in order to authenticate my API requests to the online accounting software Exact Online.
However, I'm stuck at step 3, where I use the authorization code returned in step 2 to obtain the access token.
Here's what I'm trying:
require 'httparty'
EXACT_CLIENT_ID = '<REDACTED>'
EXACT_CLIENT_SECRET = '<REDACTED>'
EXACT_SERVER_BASE_URL = 'https://start.exactonline.nl'
EXACT_AUTH_CODE = '<REDACTED>'
response = HTTParty.post("#{EXACT_SERVER_BASE_URL}/api/oauth2/token", headers: {'Content-Type' => 'application/x-www-form-urlencoded'}, query: {code: EXACT_AUTH_CODE, redirect_uri: 'http://<REDACTED>.runscope.net/', grant_type: 'authorization_code', client_id: EXACT_CLIENT_ID, client_secret: EXACT_CLIENT_SECRET})
puts response
# => 'Bad request'
puts response.code
# => 400
I don't understand why this is happening. When looking at the list of response codes here, the code means:
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
What am I doing wrong?
Update #1:
I've also tried:
response_2 = HTTParty.post("#{EXACT_SERVER_BASE_URL}/api/oauth2/token", :headers => {'Content-Type' => 'application/x-www-form-urlencoded'}, :body => {'code' => EXACT_AUTH_CODE, 'redirect_uri' => 'http://<REDACED>.runscope.net/', 'grant_type' => 'authorization_code', 'client_id' => EXACT_CLIENT_ID, 'client_secret' => EXACT_CLIENT_SECRET})
But the response is the same.
Even though using the HTTP POST method you're providing the values as query parameters by supplying them to the query: parameter to the .post method. Instead you should provide them in the body: parameter, see: How can I implement this POST request using HTTParty?
Also your syntax re. body: :body should be fixed, so it would look like:
response_2 = HTTParty.post("#{EXACT_SERVER_BASE_URL}/api/oauth2/token", :headers => {'Content-Type' => 'application/x-www-form-urlencoded'}, :body => {'code' => EXACT_AUTH_CODE, 'redirect_uri' => 'http://<REDACED>.runscope.net/', 'grant_type' => 'authorization_code', 'client_id' => EXACT_CLIENT_ID, 'client_secret' => EXACT_CLIENT_SECRET})
Last but not least: the code value is one-time usage only and has a short lifetime; make sure you use a freshly obtained one.

How can I implement this POST request using HTTParty?

I am having difficulty making a POST request to an API endpoint using Ruby's HTTParty library. The API I'm interacting with is the Gittip API and their endpoint requires authentication. I have been able to successfully make an authenticated GET request using HTTParty.
You can see in the example code:
user = "gratitude_test"
api_key = "5962b93a-5bf7-4cb6-ae6f-aa4114c5e4f2"
# I have included real credentials since the above is merely a test account.
HTTParty.get("https://www.gittip.com/#{user}/tips.json",
{ :basic_auth => { :username => api_key } })
That request works and returns the following as expected:
[
{
"amount" => "1.00",
"platform" => "gittip",
"username" => "whit537"
},
{
"amount" => "0.25",
"platform" => "gittip",
"username" => "JohnKellyFerguson"
}
]
However, I have been unable to make a successful POST request using HTTParty. The Gittip API describes making a POST request using curl as follows:
curl https://www.gittip.com/foobar/tips.json \
-u API_KEY: \
-X POST \
-d'[{"username":"bazbuz", "platform":"gittip", "amount": "1.00"}]' \
-H"Content-Type: application/json"
I have tried (unsuccessfully) structuring my code using HTTParty as follows:
user = "gratitude_test"
api_key = "5962b93a-5bf7-4cb6-ae6f-aa4114c5e4f2"
HTTParty.post("https://www.gittip.com/#{user}/tips.json",
{
:body => [ { "amount" => "0.25", "platform" => "gittip", "username" => "whit537" } ],
:basic_auth => { :username => api_key },
:headers => { 'Content-Type' => 'application/json' }
})
The first argument is the url and the second argument is an options hash. When I run the code above, I get the following error:
NoMethodError: undefined method `bytesize' for [{"amount"=>"0.25", "platform"=>"gittip", "username"=>"whit537"}]:Array
from /Users/John/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http/generic_request.rb:179:in `send_request_with_body'
I have tried various other combinations of structuring the API call, but can't figure out how to get it to work. Here's another such example, where I do not user an array as part of the body and convert the contents to_json.
user = "gratitude_test"
api_key = "5962b93a-5bf7-4cb6-ae6f-aa4114c5e4f2"
HTTParty.post("https://www.gittip.com/#{user}/tips.json",
{
:body => { "amount" => "0.25", "platform" => "gittip", "username" => "whit537" }.to_json,
:basic_auth => { :username => api_key },
:headers => { 'Content-Type' => 'application/json' }
})
Which returns the following (a 500 error):
<html>
<head>
<title>500 Internal Server Error</title>
</head>
<body>\n Internal server error, program!\n <pre></pre>
</body>
</html>
I'm not really familiar with curl, so I'm not sure if I am incorrectly translating things to HTTParty.
Any help would be appreciated. Thanks.
Just a guess, but it looks like you're passing a hash in the body when JSON is expected.
Try replacing the :body declaration with:
:body => [{ "amount" => "0.25",
"platform" => "gittip",
"username" => "whit537" }].to_json
Edit:
I suggested the to_json serializer, but misplaced it by putting it after the hash instead of the array and removing the array altogether. The example uses multiple records, so the array is necessary.
After looking at this thread, it looks like Gittip is picky about the accept header.
:headers => { 'Content-Type' => 'application/json', 'Accept' => 'application/json'}
So, the full suggestion is:
HTTParty.post("https://www.gittip.com/#{user}/tips.json",
{
:body => [ { "amount" => "0.25", "platform" => "gittip", "username" => "whit537" } ].to_json,
:basic_auth => { :username => api_key },
:headers => { 'Content-Type' => 'application/json', 'Accept' => 'application/json'}
})

Ruby rest-client file upload as multipart form data with basic authenticaion

I understand how to make an http request using basic authentication with Ruby's rest-client
response = RestClient::Request.new(:method => :get, :url => #base_url + path, :user => #sid, :password => #token).execute
and how to post a file as multipart form data
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb')
but I can't seem to figure out how to combine the two in order to post a file to a server which requires basic authentication. Does anyone know what is the best way to create this request?
How about using a RestClient::Payload with RestClient::Request...
For an example:
request = RestClient::Request.new(
:method => :post,
:url => '/data',
:user => #sid,
:password => #token,
:payload => {
:multipart => true,
:file => File.new("/path/to/image.jpg", 'rb')
})
response = request.execute
RestClient API seems to have changed. Here's the latest way to upload a file using basic auth:
response = RestClient::Request.execute(
method: :post,
url: url,
user: 'username',
password: 'password',
timeout: 600, # Optional
payload: {
multipart: true,
file: File.new('/path/to/file, 'rb')
}
)
The newest best way may be that:
the link is enter link description here
RestClient.post( url,
{
:transfer => {
:path => '/foo/bar',
:owner => 'that_guy',
:group => 'those_guys'
},
:upload => {
:file => File.new(path, 'rb')
}
})
Here is an example with a file and some json data:
require 'rest-client'
payload = {
:multipart => true,
:file => File.new('/path/to/file', 'rb'),
:data => {foo: {bar: true}}.to_json
}
r = RestClient.post(url, payload, :authorization => token)

Does the twilio-ruby gem take parameters other than 'from', 'to', and 'url' when making calls?

Looking at https://github.com/twilio/twilio-ruby/blob/master/lib/twilio-ruby/rest/calls.rb, it appears that only 'from', 'to', and 'url' are used. How do I pass a value for 'IfMachine'? For example, the following doesn't seem to work.
# set ACCOUNT_SID and AUTH_TOKEN
twilioClient = Twilio::REST::Client.new(ACCOUNT_SID, AUTH_TOKEN)
twilioAccount = twilioClient.account
twilioAccount.calls.create({
:from => 'from_number',
:to => 'to_number',
:url => '/url',
'IfMachine' => 'Hangup'
})
# IfMachine parameter is not passed in the above request
Setting 'IfMachine' => 'hangup' works for me. I think the Twilio API is case-sensitive with respect to parameter values. So 'hangup' would work but 'Hangup' probably won't.
Here's my twilio-ruby session showing that the parameters are passed correctly:
irb(main):002:0> c.account.calls.create :from => '2158377932', :to => '4159334335', :url => 'http://demo.twilio.com/welcome/voice', 'IfMachine' => 'continue'
warning: peer certificate won't be verified in this SSL session
=> <Twilio::REST::Call #uri=/2010-04-01/Accounts/AC8faaf6f7efb9dfd60bc0ff3aa7fa00be/Calls/CA0165c3b35c934ed5a2b7a87f343544ff>
irb(main):003:0> c.last_request
=> #<Net::HTTP::Post POST>
irb(main):004:0> req = c.last_request
=> #<Net::HTTP::Post POST>
irb(main):005:0> req.body
=> "IfMachine=continue&To=4159334335&Url=http%3a%2f%2fdemo.twilio.com%2fwelcome%2fvoice&From=2158377932"
I believe if you set it as a symbol instead it should work:
# set ACCOUNT_SID and AUTH_TOKEN
twilioClient = Twilio::REST::Client.new(ACCOUNT_SID, AUTH_TOKEN)
twilioAccount = twilioClient.account
twilioAccount.calls.create({
:from => 'from_number,
:to => 'to_number',
:url => '/url',
:if_machine => 'Hangup'
})

Resources