Api Requests with Ruby gem Typhoeus - ruby

What is wrong with the following request?
request = Typhoeus::Request.new("http://fluidsurveys.com/api/v2/groups",
method: :get,
userpwd: "test_user:test_password",
headers: { 'ContentType' => "application/json"})
response = request.body
puts response
This returns undefined method body for #<Typhoeus::Request:0x007f8e50d3b1d0> (NoMethodError)
The following request works fine with httparty:
call= "/api/v2/groups/"
auth = {:username => "test_user", :password => "test_password"}
url = HTTParty.get("http://fluidsurveys.com/api/v2/groups",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' } )
response = url.body
puts response
EDIT:
I tried this:
response = request.response
puts response.body
with no luck. I receive this : undefined method body for nil:NilClass (NoMethodError)

From https://github.com/typhoeus/typhoeus
You need to do the get before the response body is available.
EDIT: Here is an operable solution. It doesn't use your website, which I couldn't access even manually. But, this returns response code 200 and the response_body. Running this in my debugger showed the complete response, which you could see using "puts response.inspect".
class TyphoeusTry
require 'typhoeus'
request = Typhoeus::Request.new("http://www.google.com",
method: :get,
userpwd: "test_user:test_password",
headers: { ContentType: "application/json"})
response = request.run
puts response.response_body
end

The problem is that you didn't actually execute your request. The following code should work.
request = Typhoeus::Request.new("http://fluidsurveys.com/api/v2/groups",
method: :get,
userpwd: "test_user:test_password",
headers: { 'ContentType' => "application/json"})
request.run
response = request.response
response_code = response.code
response_body = response.body

Related

Ruby base_auth with net/http - undefined method `user' for String

I've got pure Ruby app where I want to create request to external API. To do so I'm using standard Ruby Net::HTTP like below:
require 'net/http'
require 'uri'
class Api
BASE_URI = 'https://staging.test.com'
WORKFLOW = 'tests'
QUIZ_PATH = "/v3/accounts/workflows/#{WORKFLOW}/conversations"
def initialize(payload:)
#payload = payload
end
def post_quiz
handle_response(Net::HTTP.post_form("#{BASE_URI}#{QUIZ_PATH}", options))
end
attr_reader :payload
private
def options
{
basic_auth: basic_auth,
body: payload.to_json,
headers: headers
}
end
def basic_auth
{
username: Settings.ln_username,
password: Settings.ln_password
}
end
def headers
{
'User-Agent' => 'Mozilla/5.0',
'Accept-Language' => 'en-US,en;q=0.5',
'Content-Type' => 'application/json'
}
end
def handle_response(response)
return response.body if response.success?
end
end
But instead of response I'm getting an error:
NoMethodError: undefined method `user' for #String:0x00007f80eef9e6f8
Did you mean? super
/Users/usr/.rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/net/http.rb:527:in `post_form'
I don't have any user there, what is it?
Net::HTTP.post_form is used to send FormData pairs - its not what you want to send JSON and it doesn't even allow you to send headers (You're actually putting them in the request body!).
If you want to send a POST request with HTTP Basic auth and custom headers and JSON body you need to create the request object manually:
require 'net/http'
require 'uri'
class Api
BASE_URI = 'https://staging.test.com'
WORKFLOW = 'tests'
QUIZ_PATH = "/v3/accounts/workflows/#{WORKFLOW}/conversations"
attr_reader :payload
def initialize(payload:)
#payload = payload
end
def post_quiz
url = URI.join(BASE_URI, QUIZ_PATH)
request = Net::HTTP::Post.new(url, headers)
request.basic_auth = Settings.ln_username, Settings.ln_password
request.body = #payload.to_json
# open a connection to the server
response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|
http.request(request)
end
handle_response(response)
end
private
def headers
{
'User-Agent' => 'Mozilla/5.0',
'Accept-Language' => 'en-US,en;q=0.5',
'Content-Type' => 'application/json'
}
end
# How to respond from an API client is a whole topic in itself but a tuple or hash might
# be a better choice as it lets consumers decide what to do with the response and handle stuff like logging
# errors
def handle_response(response)
# Net::HTTP doesn't have a success? method - you're confusing it with HTTParty
case response
when Net::HTTPSuccess, Net::HTTPCreated
response.body
else
false
end
end
end
Here is the source code that raises the error:
def HTTP.post_form(url, params)
req = Post.new(url)
req.form_data = params
>> req.basic_auth url.user, url.password if url.user
start(url.hostname, url.port,
:use_ssl => url.scheme == 'https' ) {|http|
http.request(req)
}
end
From the docs:
post_form(url, params)
Posts HTML form data to the specified URI object. The form data must be provided as a Hash mapping from String to String.
That means Net::HTTP.post_form(URI("#{BASE_URI}#{QUIZ_PATH}"), options) fixes it. You are currently sending a string as url instead of a URI.

In ruby with sinatra, How to get I response with get method on rest client?

I use ruby with sinatra and I used rest-client on import for payment.
I got token that string typed through post method on specific url: '... /users/getToken'.
Using this token, I wanna get payments information with get method on this url:
get_url = 'https://api/iamport.kr/payments/'+imp_uid
the detail codes are below,
def get_paymentsdetails(token, imp_uid)
get_url = 'https://api.iamport.kr/payments/'+imp_uid
response = RestClient.get get_url, :data => {}.to_json, :accept => :json, :headers => {'Authorization' => token}
json = JSON.parse(response, :symbolize_names => true)
# json = JSON.parse(response.to_json, {:symbolize_names => true})
return json
end
However, I got 401 unauthorized error on this part of code.
response = RestClient.get get_url, :data => {}.to_json, :accept => :json, :headers => {'Authorization' => token}
After I access get_url with specific imp_uid, I got this page,{"code":-1,"message":"Unauthorized","response":null}
I checked parameter token and imp_uid of get_paymentsdetails function have valid string values,, so How can I access response parameter??
I think that there are some problems on response = RestClient.get get_url.... code.
Thanks.
Method 'get' from the 'RestClient' class return some object with attributes. So response have few values. Which of them do you need? Access to them you can get by their names, its described here.
In your case, after response = RestClient.get get_url... you should have variable response and ability to call response.headers, response.code or response.body.
But im afraid that you have some problems with autorization, which means that imp_uid or token is not correct. Thats why remote server sended to you responce with http-code 401 (Unauthorized). If it is so you should try to check your imp_uid and token. If everything is correct try to reach support of iamport.kr .

Ruby HTTP sending API key Basic_auth

I have been following a tutorial on GitHub Pages and
I am trying to pass an Apikey to a webservice as basic auth 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' for example but I cannot work out how to implement it into the method, or even if that is the proper place for it.
I have a class called connection with my request method in it. I need to post 'apiKey' as header and not in the body. I have read the ruby docs but I cannot work out how to apply it to this specific class.
require "net/http"
require "uri"
require "ostruct"
require "json"
class Connection
ENDPOINT = "http://localhost"
APP_LOCATION = "/task_manager/v1/"
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
def initialize(endpoint = ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def get(path, params)
request_json :get, path, params
end
def post(path, params)
request_json :post, APP_LOCATION + path, params
end
def put(path, params)
request_json :put, path, params
end
def delete(path, params)
request_json :delete, path, params
end
private
def request_json(method, path, params)
response = request(method, path, params)
body = JSON.parse(response.body)
OpenStruct.new(:code => response.code, :body => body)
rescue JSON::ParserError
response
end
def request(method, path, params = {})
case method
when :get
full_path = encode_path_params(path, params)
request = VERB_MAP[method.to_sym].new(full_path)
else
request = VERB_MAP[method.to_sym].new(path)
request.set_form_data(params)
end
#http.request(request)
end
def encode_path_params(path, params)
encoded = URI.encode_www_form(params)
[path, encoded].join("?")
end
end
If I post to the server using Advanced Rest Client and put the apikey in the
http://localhost/task_manager/v1/tasks?=
header
Authorization: 9c62acdda8fe12507a435345bb9b2338
and in the body
email=free%40mail.com&password=free&task=test
then I get
{
error: false
message: "Task created successfully"
task_id: 5
}
So how can I post it using this class?.
connection = Connection.new
result = connection.post("task", {'task' => 'task'})
Basic Authentication example:
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass'
http://docs.ruby-lang.org/en/trunk/Net/HTTP.html#class-Net::HTTP-label-Basic+Authentication
Or if you want to add a raw Authorization header in your request method you can do
request.add_field 'Authorization', 'huda7da97hre3rhr1yrh0130409u1u'
But basic authentication normally means that there is a user name and a password. With your API key - I am not sure you actually need basic authentication. I do not know what you API actually requires but if you have not tried it yet you can try sending the api key as an additional parameter
result = connection.post("register", {'email' => email, 'name' => name, 'password' => password, 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' })

How to invoke HTTP POST method over SSL in ruby?

So here's the request using curl:
curl -XPOST -H content-type:application/json -d "{\"credentials\":{\"username\":\"username\",\"key\":\"key\"}}" https://auth.api.rackspacecloud.com/v1.1/auth
I've been trying to make this same request using ruby, but I can't seem to get it to work.
I tried a couple of libraries also, but I can't get it to work.
Here's what I have so far:
uri = URI.parse("https://auth.api.rackspacecloud.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new("/v1.1/auth")
request.set_form_data({'credentials' => {'username' => 'username', 'key' => 'key'}})
response = http.request(request)
I get a 415 unsupported media type error.
You are close, but not quite there. Try something like this instead:
uri = URI.parse("https://auth.api.rackspacecloud.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new("/v1.1/auth")
request.add_field('Content-Type', 'application/json')
request.body = {'credentials' => {'username' => 'username', 'key' => 'key'}}.to_json
response = http.request(request)
This will set the Content-Type header as well as post the JSON in the body, rather than in the form data as your code had it. With the sample credentials, it still fails, but I suspect it should work with real data in there.
There's a very good explanation of how to make a JSON POST request with Net::HTTP at this link.
I would recommend using a library like HTTParty. It's well-documented, you can just set up your class like so:
class RackSpaceClient
include HTTParty
base_uri "https://auth.api.rackspacecloud.com/"
format :json
headers 'Accept' => 'application/json'
#methods to do whatever
end
It looks like the main difference between the Ruby code you placed there, and the curl request, is that the curl request is POSTing JSON (content-type application/json) to the endpoint, whereas request.set_form_data is going to send a form in the body of the POST request (content-type application/x-www-form-urlencoded). You have to make sure the content going both ways is of type application/json.
All others are too long here is a ONE LINER:
Net::HTTP.start('auth.api.rackspacecloud.com', :use_ssl => true).post(
'/v1.1/auth', {:credentials => {:username => "username",:key => "key"}}.to_json,
initheader={'Content-Type' => 'application/json'}
)
* to_json needs require 'json'
OR if you want to
NOT verify the hosts
be more readable
ensure the connection is closed once you're done
then:
ssl_opts={:use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE}
Net::HTTP.start('auth.api.rackspacecloud.com', ssl_opts) { |secure_connection|
secure_connection.post(
'/v1.1/auth', {:credentials => {:username => "username",:key => "key"}}.to_json,
initheader={'Content-Type' => 'application/json'}
)
}
In case it's tough to remember what params go where:
SSL options are per connection so you specify them while opening the connection.
You can reuse the connection for multiple REST calls to same base url. Think of thread safety of course.
Header is a "request header" and hence specified per request. I.e. in calls to get/post/patch/....
HTTP.start(): Creates a new Net::HTTP object, then additionally opens the TCP connection and HTTP session.
HTTP.new(): Creates a new Net::HTTP object without opening a TCP connection or HTTP session.
Another example:
#!/usr/bin/ruby
require 'net/http'
require 'json'
require 'uri'
full_url = "http://" + options[:artifactory_url] + "/" + "api/build/promote/" + options[:build]
puts "Artifactory url: #{full_url}"
data = {
status: "staged",
comment: "Tested on all target platforms.",
ciUser: "builder",
#timestamp: "ISO8601",
dryRun: false,
targetRepo: "#{options[:target]}",
copy: true,
artifacts: true,
dependencies: false,
failFast: true,
}
uri = URI.parse(full_url)
headers = {'Content-Type' => "application/json", 'Accept-Encoding'=> "gzip,deflate",'Accept' => "application/json" }
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.basic_auth(options[:user], options[:password])
request.body = data.to_json
response = http.request(request)
puts response.code
puts response.body

Ruby HTTP Post Request - Content Type?

I have the following Method:
http_client=Net::HTTP.new(#uri.host,#uri.port)
request=Net::HTTP::Post.new(#uri.request_uri)
request.set_form_data(params)
request.basic_auth(#user,#pass)
result = http_client.request(request)
#
# !!! .content_type does not exist !!!
#
result=case result.content_type
when "application/json" JSON.parse(result,{:symbolize_names => true})
when "application/xml" XMLHELPER.parse(result)
end
result
So how to determine the content-type that was sent by the server?
You're getting a NoMethodError from calling result.content_type because you've assigned result to the response body, which is a string.
[...]
response = client.request(request)
result = case response.content_type
when "application/json" JSON.parse(response.body, {:symbolize_names => true})
when "application/xml" XMLHELPER.parse(response.body)
end

Resources