HTTPs Request in Ruby with parameters - ruby

I'm trying to pull data from a RESTful JSON web service which uses https. They provided a Curl example which works no problem; however, I'm trying to run the query in Ruby and I'm not a Ruby developer.
Any help much appreciated!
cURL example:
curl -G "https://api.example.com/v1/query/" \
-H "Accept: application/vnd.example.v1+hal+json" \
-u "$API_KEY:$API_SECRET" \
-d "app_id=$APP_ID" \
-d "days=3" \
-d "metrics=users" \
-d "dimensions=day"
My attempt in Ruby which is resulting in a HTTPUnauthorized 401:
require 'net/https'
require 'uri'
# prepare request
uri = URI.parse("https://api.example.com/v1/query/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri, {
'Accept' => 'application/vnd.example.v1+hal+json',
'api_key' => 'api_secret',
'app_id' => 'app_id',
'days' => '3',
'metrics' => 'users',
'dimensions' => 'day'})
response = http.request(request)
response.body
response.status
response["header-here"] # All headers are lowercase
# Analyze the response
if response.code != "200"
puts "Error (status-code: #{response.code})\n#{response.body}"
print 0
else
print 1
end
** Update **
As per feedback below, I've installed Typhoeus and updated the request. Now I'm getting through. Thanks all!
request = Typhoeus::Request.new(
"https://api.example.com/v1/query/",
userpwd: "key:secret",
params: {
app_id: "appid",
days: "3",
metrics: "users",
dimensions: "day"
},
headers: {
Accept: "application/vnd.example.v1+hal+json"
}
)

First you need to realize that:
'Accept' => 'application/vnd.example.v1+hal+json'
is a header, not a parameter.
Also:
$API_KEY:$API_SECRET
is basic HTTP authentication, not a parameter.
Then, take my advice and go with a better Ruby HTTP client:
https://github.com/lostisland/faraday (preferred, a wrapper for the bellow)
https://github.com/typhoeus/typhoeus
https://github.com/geemus/excon
Update:
Try the following from IRB:
Typhoeus::Config.verbose = true # this is useful for debugging, remove it once everything is ok.
request = Typhoeus::Request.get(
"https://api.example.com/v1/query/",
userpwd: "key:secret",
params: {
app_id: "appid",
days: "3",
metrics: "users",
dimensions: "day"
},
headers: {
Accept: "application/vnd.example.v1+hal+json"
}
)

curl's -u sends the Authorization header. so your 'api_key' => 'api_secret', should be replaced with this one(once again, its http header, not parameter).
"Authorization" => "Basic ".Base64.encode64("api_key:api_secret")
## require "base64"

Related

make a request using curl, equivalent to a REST_CLIENT request

I'm using the following request in Curl
curl -X GET -k -H 'Content-Type: application/json' -H 'Authorization: Bearer XXXXXXX' -H 'RowId: 100' -i 'https://url.xxx/row'
and the request using REST_CLIENT (2.1.0):
RestClient::Request.execute(:url => "https://url.xxx/row", :method => :get, :verify_ssl => false, :headers => {:content_type => "application/json", :Authorization => "Bearer XXXXXXX", :RowId => 100})
RestClient::NotFound: 404 Not Found
The first one (Curl) is working, but the equivalent request in RestClient does not.
The problem is that rest-client is not passing all headers:
{:content_type => "application/json", :Authorization => "Bearer XXXXXXX", :RowId => 100}
only content_type and Authorization are used, the others are not taken when request is sending
There is a similar issue with net/http:
require 'net/http'
uri = URI('https://url.xxx/row')
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.ssl_version = :TLSv1
req = Net::HTTP::Get.new uri
req['content_type'] = 'application/json'
req['Authorization'] = "Bearer #{token}"
req['RowId'] = 100
res = https.request req
puts res.body
Any suggestions ?
Thx
rest-client will return 404 error in multiple cases not only for not found errors
the best way here is to return and check the server's response
as per their documentation: https://github.com/rest-client/rest-client#exceptions-see-httpwwww3orgprotocolsrfc2616rfc2616-sec10html
>> RestClient.get 'http://example.com/nonexistent'
Exception: RestClient::NotFound: 404 Not Found
>> begin
RestClient.get 'http://example.com/nonexistent'
rescue RestClient::ExceptionWithResponse => e
e.response
end
=> <RestClient::Response 404 "<!doctype h...">
probably also try to call e.response.body to check the error

Executing curl through Ruby script

I'm trying to execute curl through Ruby script using two different methods and have some errors in both.
First method is using shell command
#!/usr/bin/ruby
`curl --cacert RepoCert --location --request POST 'https://test-service/rest/services/request/' --header 'Authorization: Basic amkkdksmmkk3XCf45DffSa23Ert' --header 'Content-Type: application/json' --data-binary "{ \"serviceID\": \"3\", \"TypeId\": \"52\", \"requestFieldValues\": { \"summary\": \"summary\", \"description\": \"something\", \"prority\": { \"id\": \"1\"}, \"customfield\": \"5\" }} "`
and I have an error
"Unexpected character (\u0027s\u0027 (code 115)): was expecting double-quote to start field name\n at [Source: com.itlab.jira.plugins.extender.helper.RequestWrapper$5db70c75; line:1, column:5]"
Also tried using Ruby Net::HTTP (code generated from https://jhawthorn.github.io/curl-to-ruby/)
require 'net/http'
require 'uri'
require 'json'
uri = URI.parse("https://test-service/rest/services/request/")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/json"
request["Authorization"] = "Basic amkkdksmmkk3XCf45DffSa23Ert"
request.body = JSON.dump({
"serviceID" => "3",
"TypeId" => "52",
"requestFieldValues" => {
"summary" => "summary",
"description" => "something",
"prority" => {
"id" => "1"
},
"customfield" => "5"
}
})
req_options = {
use_ssl: uri.scheme == "https",
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
and I get
/usr/share/ruby/net/http.rb:921:in `connect': SSL_connect returned=1 errno=0 state=error: certificate verify failed (OpenSSL::SSL::SSLError)
The problem is I have to use certificate authorization in this case so I can't ignore it.
Curl executed form command line is working fine. Any idea how to fix one of those methods (or both)?
Thanks guys for trying to help but I figure it out using Net::HTTP. I leave comment here if someone ever looked for a similar case.
require 'net/http'
require 'uri'
require 'json'
uri = URI.parse("https://test-service/rest/services/request/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = "/directory/to/cert/file"
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = "application/json"
request["Authorization"] = "Basic amkkdksmmkk3XCf45DffSa23Ert"
request.body = JSON.dump({
"serviceID" => "3",
"TypeId" => "52",
"requestFieldValues" => {
"summary" => "summary",
"description" => "something",
"prority" => {
"id" => "1"
},
"customfield" => "5"
}
})
response = http.request(request)

How to use httparty instead of net/http or curl

I have a curl request which works
curl -X GET -k 'https://APIADDRESSHERE' -u 'USERNAME:PASSWORD' -H 'Content-Type: application/json'
I can also get this working in ruby:
require 'net/http'
require 'uri'
require 'openssl'
uri = URI.parse("https://APIADDRESSHERE")
request = Net::HTTP::Get.new(uri)
request.basic_auth("USERNAME", "PASSWORD")
request.content_type = "application/json"
req_options = {
use_ssl: uri.scheme == "https",
verify_mode: OpenSSL::SSL::VERIFY_NONE,
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
However when I try this with httparty I keep getting a 401 error saying:
An Authentication object was not found in the SecurityContext This request requires HTTP authentication.
I have tried a lot of variations of the following but I'm stuck.
response = HTTParty.get('https://APIURLHERE', {"headers": { "Authorization:" => "BASE64ENCODEDUSERNAMEANDPASSWORD", "Content-Type" => "application/json" }})
Have you tried the following?
response = HTTParty.get('https://APIURLHERE', headers: { "Authorization:" => "BASE64ENCODEDUSERNAMEANDPASSWORD", "Content-Type" => "application/json" })
Ok I made a simple mistake somewhere along the way because the original solution from the similar post worked... Dont know how it didnt work 50 times last time I was working on this but it was clearly an error on my part somewhere.

How to POST to exchange a 23andme auth code for a token

The 23andme web site has an API and they give the following instructions:
Send a POST /token/ request with these parameters (client_id and client_secret are on your dashboard):
curl https://api.23andme.com/token/
-d client_id=xxx \
-d client_secret=yyy \
-d grant_type=authorization_code \
-d code=zzz \
-d "redirect_uri=https://localhost:5000/receive_code/"
-d "scope=basic%20rs3094315"
If successful, you'll get a 200 JSON response like this:
{"access_token":"89822b93d2",
"token_type":"bearer",
"expires_in": 86400,
"refresh_token":"33c53cd7bb",
"scope":"rs3094315 basic"}
So, here's what I tried, but it didn't work. I know Ruby, but have never used net/http or uri.
def self.get_token(code)
uri = URI.parse("https://api.23andme.com/token")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request["client_id"] = 'my client id goes here'
request["client_secret"] = 'my client secret goes here'
request["grant_type"] = 'authorization_code'
request["code"] = code
request["redirect_uri"] = 'http://localhost:3000'
request["scope"] = 'names basic haplogroups relatives'
response = https.request(request)
return response.to_s
end
You are doing it right using curl with the -d option to set the parameter in the body of the POST request. However, with the Net::HTTP::Post object, the syntax request["key"] = value is used to set the headers of the Http object.
Use set_form_data to set all the parameters into the body of the POST request.
For example, use it in this way:
request.set_form_data({"client_id" => "my client id goes here", "code" => code})
The following will clarify the above:
$ > request["client_id"] = 'my client id goes here'
# => "my client id goes here"
$ > request.body
# => nil # body is nil!!!
$ > request.each_header { |e| p e }
# "client_id"
# => {"client_id"=>["my client id goes here"]}
$ > request.set_form_data("client_secret" => 'my client secret goes here')
# => "application/x-www-form-urlencoded"
$ > request.body
# => "client_secret=my+client+secret+goes+here" # body contains the added param!!!
I just wanted to post the code that worked. Thanks to Rafa.
def self.get_token(code)
uri = URI.parse("https://api.23andme.com/token")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.path,
initheader = {'Content-Type' =>'application/json'})
request.set_form_data({"client_id" => 'my client id',
"client_secret" => 'my client secret',
"grant_type" => 'authorization_code',
"code" => code,
"redirect_uri" => 'http://localhost:3000/receive_code/',
"scope" => 'names basic haplogroups relatives'})
response = https.request(request)
data = JSON.load response.read_body
return data["access_token"]
end

Rest-Client: how to post multipart/form-data?

I have to implement the curl POST request below listed, in Ruby, using Rest-Client.
I have to:
send params in header;
send params (that do not contain a file) as multipart/form-data:
$ curl -X POST -i -H "Authorization: Bearer 2687787877876666686b213e92aa3ec7e1afeeb560000000001" \
https://api.somewhere.com/endpoint -F sku_id=608399
How can I translate the curl request using the RestClient rubygem?
Reading documentation (multipart paragraph): https://github.com/rest-client/rest-client
I coded as:
#access_token = 2687787877876666686b213e92aa3ec7e1afeeb560000000001
url = 'https://api.somewhere.com/endpoint'
req = { authorization: "Bearer #{#access_token}"}
RestClient.post url, req, {:sku_id => 608399, :multipart => true}
But I get a server error; is the Ruby code above correct?
Thanks a lot,
Giorgio
Since I had trouble understanding the example Dmitry showed, here is an example for creating a Multipart request to upload an image:
response = RestClient.post 'https://yourhost.com/endpoint',
{:u_id => 123, :file => File.new('User/you/D/cat.png', 'rb'), :multipart => true},
{:auth_token => xyz5twblah, :cookies => {'_cookie_session_name' => cookie}}
It's code not valid for RestClient implementation.
headers should follow after payload.
module RestClient
def self.post(url, payload, headers={}, &block)
...
end
end
UPDATE
#access_token should be a string "2687787877876666686b213e92aa3ec7e1afeeb560000000001"
then
RestClient.log = 'stdout'
RestClient.post url, {:sku_id => 608399, :multipart => true}, req
and log
RestClient.post "https://api.somewhere.com/endpoint", "--330686\r\nContent-Disposition: form-data; name=\"sku_id\"\r\n\r\n608399\r\n--330686--\r\n", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Authorization"=>"Bearer 2687787877876666686b213e92aa3ec7e1afeeb560000000001", "Content-Length"=>"79", "Content-Type"=>"multipart/form-data; boundary=330686"

Resources