Rails Ajax -> Sinatra -> Amazon API and back - ruby

I'm not sure that I really understand how Sinatra works.
I'd like to get some products from Amazon using their API, in my Rails app. But HTTP requests are blocking the IO. I got the tip to create a Sinatra app and make an Ajax request to there instead.
Ajax: (From my Rails app)
$.ajax({
url: "http://sinatra.mydomain.com",
dataType: "json",
success: function(data) {
console.log(data);
}
});
Sinatra app: (I also make use of the Sinatra-synchrony gem)
require 'sinatra'
require 'sinatra/synchrony'
require 'erb'
require 'rest-client'
require 'amazon_product'
Sinatra::Synchrony.overload_tcpsocket!
get '/' do
req = AmazonProduct["us"]
req.configure do |c|
c.key = "KEY"
c.secret = "SECRET"
c.tag = "TAG"
end
req << { :operation => 'ItemSearch',
:search_index => "DVD",
:response_group => %w{ItemAttributes Images},
:keywords => "nikita",
:sort => "" }
resp = req.get
#item = resp.find('Item').shuffle.first
erb :layout, :locals => { :amazon_product => #item }
end
Layout.erb: (renders fine if I go to this Url in the browser)
<%= amazon_product %>
Problem:
My Ajax response is a 200 OK but with an empty response.
I'm can't figure out what's wrong. Please advise.

It seems that you've faced with ajax 'cross-domain security' problem. Try to use JSONP (JSON with padding).
Change your sinatra get handler:
get '/' do
req = AmazonProduct["us"]
req.configure do |c|
c.key = KEY
c.secret = SECRET
c.tag = TAG
end
req << { :operation => 'ItemSearch',
:search_index => "DVD",
:response_group => %w{ItemAttributes Images},
:keywords => "nikita",
:sort => "" }
resp = req.get
#item = resp.find('Item').shuffle.first
content_type :json
callback = params.delete('callback') # jsonp
json = #item.to_json
if callback
content_type :js
response = "#{callback}(#{json})"
else
content_type :json
response = json
end
response
end
And change your Ajax request:
$.getJSON("http://address_of_sinatra?callback=?",
function(data) {
console.log(data);
});
Or you can add dataType: 'jsonp' to your $.ajax request.
After that you should see data object in js debugger (at least it's working in my case :D )

Related

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' })

Api Requests with Ruby gem Typhoeus

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

RestClient.put error in Ruby

I am trying to do a RestClient.put in Ruby for a highchart graphic but I see that this is not replicated. Also first I have a RestClient.post and works fine but the problem is then with the put. At the end I want to manage an exception because the put returns 302 but I want to manage it as OK. If then of the post I make de put manually with the restclient plugin in the browser works fine, for this I assume that the problem is in the script at the put.
This is the fragment;
date_post = Time.now
date_post = fecha_post.strftime("%Y-%m-%d")
date_unix = Time.now
date_unix = fecha_unix.strftime("%Y-%m-%d")
date_unix = (fecha_unix.to_time.to_i)
date_unix = fecha_unix.to_s + '000'
conversion = 50 #this is a example number
name_metric= "metric_example"
build_header = {:content_type => :json, :accept => :json, :Authorization=> "here_is_auth_id"}
url = "http://example.com/data.json"
params = <<-eos
{
"identifier":"#{name_metric}","date":"#{date_post}","value":[#{date_unix},[#{conversion}]]
}
eos
JSON.parse RestClient.post url, params, build_header
begin
JSON.parse RestClient.put 'http://example', params, {:content_type => :json}
rescue => e
e.response
end
Could you tell me why is not working correctly?
Thanks in advance.

How to POST binary data using Rest Client?

I am trying to read in an audio file of type wav or amr from a HTML form using Rest Client. I have the code to do this in PHP.
$filename = $_FILES['f1']['name'];
public function getFile($filename) {
if (file_exists($filename)) {
$file_binary = fread(fopen($filename, "r"), filesize($filename));
return $file_binary;
} else {
throw new Exception("File not found.");
}
}
I need to convert this code to Ruby and I am having trouble doing so as I am a relative novice when it comes to Ruby.
According to RestClient's repo:
def self.post(url, payload, headers={}, &block)
Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
end
This snippet of code simply sends a file:
file = File.open('path/to/file.extension', 'r')
RestClient.post("your_url_to_the_endpoint", file)
So I assume all you still need to do is to set the headers:
begin
file = File.open(params[:f1], "rb")
url = "...."
response = RestClient.post url, file, {:Authorization => "Bearer #{#access_token}", :Accept => 'application/json', :Content_Type => 'audio/wav'}
rescue => e
#error = e.message
ensure
return erb :speech
end

Ruby mechanize post with header

I have page with js that post data via XMLHttpRequest and server side script check for this header, how to send this header?
agent = WWW::Mechanize.new { |a|
a.user_agent_alias = 'Mac Safari'
a.log = Logger.new('./site.log')
}
agent.post('http://site.com/board.php',
{
'act' => '_get_page',
"gid" => 1,
'order' => 0,
'page' => 2
}
) do |page|
p page
end
I found this post with a web search (two months later, I know) and just wanted to share another solution.
You can add custom headers without monkey patching Mechanize using a pre-connect hook:
agent = WWW::Mechanize.new
agent.pre_connect_hooks << lambda { |p|
p[:request]['X-Requested-With'] = 'XMLHttpRequest'
}
ajax_headers = { 'X-Requested-With' => 'XMLHttpRequest', 'Content-Type' => 'application/json; charset=utf-8', 'Accept' => 'application/json, text/javascript, */*'}
params = {'emailAddress' => 'me#my.com'}.to_json
response = agent.post( 'http://example.com/login', params, ajax_headers)
The above code works for me (Mechanize 1.0) as a way to make the server think the request is coming via AJAX, but as stated in other answers it depends what the server is looking for, it will be different for different frameworks/js library combos.
The best thing to do is use Firefox HTTPLiveHeaders plugin or HTTPScoop and look at the request headers sent by the browser and just try and replicate that.
Seems like earlier that lambda had one argument, but now it has two:
agent = Mechanize.new do |agent|
agent.pre_connect_hooks << lambda do |agent, request|
request["Accept-Language"] = "ru"
end
end
Take a look at the documentation.
You need to either monkey-patch or derive your own class from WWW::Mechanize to override the post method so that custom headers are passed through to the private method post_form.
For example,
class WWW::Mechanize
def post(url, query= {}, headers = {})
node = {}
# Create a fake form
class << node
def search(*args); []; end
end
node['method'] = 'POST'
node['enctype'] = 'application/x-www-form-urlencoded'
form = Form.new(node)
query.each { |k,v|
if v.is_a?(IO)
form.enctype = 'multipart/form-data'
ul = Form::FileUpload.new(k.to_s,::File.basename(v.path))
ul.file_data = v.read
form.file_uploads << ul
else
form.fields << Form::Field.new(k.to_s,v)
end
}
post_form(url, form, headers)
end
end
agent = WWW::Mechanize.new
agent.post(URL,POSTDATA,{'custom-header' => 'custom'}) do |page|
p page
end

Resources