Ruby reverse image search on Yandex using rest-client file upload - ruby

I am trying to clone the top answer from this stack overflow question in ruby
Reverse search an image in Yandex Images using Python
require 'rest-client'
require 'pry'
response = RestClient.post 'https://yandex.com/images/search',
proxy: 'http://1.1.1.1:8080',
params: {rpt: 'imageview', format: 'json', request: '{"blocks":[{"block":"b-page_type_search-by-image__link"}]}'},
:upload => File.new("/home/alex/Desktop/yandex_search/t2.jpg", 'rb'),
multipart: true
binding.pry
I cannot get the file to upload. Keep getting a response
"Search results for empty request in Yandex.Images"

Based on the python implementation it looks like the params should be sent as query params.
The way to tell RestClient to send it as query parameters is to use the params value in the headers parameter.
require 'rest-client'
query_strings = {rpt: 'imageview', format: 'json', request: '{"blocks":[{"block":"b-page_type_search-by-image__link"}]}'}
RestClient.proxy = "http://1.1.1.1:8080"
result = RestClient.post(
"https://yandex.com/images/search",
{:upfile => File.new("/home/alex/Desktop/yandex_search/t2.jpg", 'rb')},
{params: query_strings, multipart: true}
)

Related

Google Signin from server side app in ruby

I have a mobile app that is signin in with google and sending a server auth code to my backend app.
I want to use this code, along with the client secrets from the google developer console, to retrieve a refresh code for retrieving data from google drive when the user is offline.
Google provides an client for auth calls in ruby, but it seems not to be maintained lately and I could not see a way to do this kind of authorisation in the docs.
In the documentation, I could find an example of how to do this on python:
from oauth2client import client
# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
CLIENT_SECRET_FILE,
['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
auth_code)
I would like to do this in ruby through a post to their https://www.googleapis.com/oauth2/v4/token endpoint. Here is what I've tried so far:
require 'uri'
require 'net/http'
require 'json'
url = URI("https://www.googleapis.com/oauth2/v4/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
headers = {'Content-Type': 'application/json'}
request = Net::HTTP::Post.new(url.request_uri, headers)
request.body = {
code: "#{server_auth_code_sent_to_api}",
client_id: "#{client_id_from_developer_console}",
client_secret: "#{client_secret_from_developer_console}",
grant_type: 'authorization_code',
redirect_url: '',
}.to_json
response = http.request(request)
puts JSON.parse(response.read_body)
But I keep getting the following error:
{
"error": "unsupported_grant_type",
"error_description": "Invalid grant_type: "
}
Does anybody has an idea on what I'm doing wrong, or has a working example on how to do this kind of authorisation?
Thanks in advance.
In case somebody stumbles here with a similar problem, what caused the request to fail was the Content-Type, and not the grant_type parameter.
Digging around in the code for the client library I saw that they use application/x-www-form-urlencoded the endpoint expects a application/x-www-form-urlencoded content type. I adjusted my code accordingly and was able to get a successful response with the valid credentials and token.
Here follows the resulting code:
require 'uri'
require 'net/http'
require 'json'
url = URI("https://www.googleapis.com/oauth2/v4/token")
params = {
"code" => "#{server_auth_code_sent_to_api}",
"client_id" => "#{client_id_from_developer_console}",
"client_secret" => "#{client_secret_from_developer_console}",
"grant_type" => "authorization_code",
"redirect_url" => "#{redirect_url_from_developer_console}",
}
response = Net::HTTP.post_form(url, params)
puts JSON.parse(response.read_body)

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 .

How to perform the following curl request using the ruby Faraday gem?

This is the curl request I'd like to perform:
curl --request GET --url 'https://us8.api.mailchimp.com/3.0/' --user 'anystring:<apikey>'
Running this curl command from the command line, I get a bunch of JSON, which is what I want.
I'm trying to perform this same request using Faraday. This is what I've tried:
conn = Faraday.new "https://us8.api.mailchimp.com/3.0/" do |faraday|
faraday.adapter Faraday.default_adapter
end
conn.basic_auth('apikey', <apikey>)
response = conn.get
puts response.body # => "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>301 Moved Permanently</title>\n</head>...
How do I get JSON instead of the html I'm seeing in response.body?
This ended up working for me:
conn = Faraday.new('https://us8.api.mailchimp.com/3.0/') do |c|
c.use FaradayMiddleware::ParseJson, content_type: "application/json"
c.use Faraday::Adapter::NetHttp
end
conn.basic_auth('apikey', <api_key>)
response = conn.get('campaigns')
puts response # => json blob
A much more concise option is to manually encode your credentials into Base64 and set the Authorization header manually. This saves a few lines of code and keeps it in a similar format to regular Faraday requests (i.e. Faraday.post <URL>, <BODY>, <HEADERS>).
i.e.
encoded_credentials = Base64.encode64('apikey:api_secret').chomp
Faraday.post 'http://myhost.local/my_url', {}, authorization: "Basic #{encoded_credentials}"
This is the farday code for curl operations using ruby gem.
def contacts
response = JSON.parse(params)
#conn = Faraday.new('https://domain.example.com/api/v2/') do |farday|
farday.use FaradayMiddleware::ParseJson, content_type: "application/json"
farday.use Faraday::Adapter::NetHttp
end
#conn.basic_auth('apikey', <your_api_key>)
response = #conn.get('contacts')
puts response
render :json => {"success" => true}
end

POST JSON response to HTTP request in Ruby

I'm running a Ruby app on Heroku. The app returns a JSON which is accessible when I go to the debugger of my browser. The JSON response is of the following template:
rates = {
"Aluminium" => price[1],
"Copper" => price_cu[1],
"Lead" => price_pb[1],
"Nickel" => price_ni[1],
"Tin" => price_sn[1],
"Zinc" => price_zn[1],
}
Sample response:
{
"Aluminium":"1765.50",
"Copper":"7379.00",
"Lead":"2175.00",
"Nickel":"14590.00",
"Tin":"22375.00",
"Zinc":"2067.00"
}
the code i wrote to achieve this is:
Test.rb
class FooRunner
def self.run!
#calculations_for_rates
rates.to_json
end
if __FILE__ == $0
puts FooRunner.run!
end
app.rb
require 'sinatra'
require './test.rb'
result = FooRunner.run!
File.open('output.json','w') do |f|
f.write result
end
content_type :json
result
When I try to access this link using
$.getJSON('app-url',function(data){
console.log(data);
});
it gives me an error saying
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Is there a way for me to directly access the JSON response by writing the JSON to the HTTP response?
So I am guessing that the page you are making the get request from is not served up by Sinatra. You can add the header Access-Control-Allow-Origin: * to that request to make it work.
This answer shows how to do it by either using response['Access-Control-Allow-Origin'] = * or headers( "Access-Control-Allow-Origin" => "*" )
That answer also lists this blog post as a reference to Cross Origin Resource Sharing in Sinatra.

Specifying Content Type in rspec

I'm trying to build an rspec test that sends JSON (or XML) via POST. However, I can't seem to actually get it working:
json = {.... data ....}.to_json
post '/model1.json',json,{'CONTENT_TYPE'=>'application/json'}
and this
json = {.... data ....}.to_json
post '/model1.json',json,{'Content-Type'=>'application/json'}
any ideas? THANKS!
In Rails 3, you can skip the header and #request.env stuff and just add a format parameter to your post call, e.g.:
post :create, format: :json, param: 'Value of Param'
There's a way to do this described in this thread -- it's a hack, but it seems to work:
#request.env["HTTP_ACCEPT"] = "application/json"
json = { ... data ... }.to_json
post :create, :some_param => json
A lot of frustration and variations and that's what worked for me.
Rails 3.2.12 Rspec 2.10
#request.env["HTTP_ACCEPT"] = "application/json"
#request.env["CONTENT_TYPE"] = "application/json"
put :update, :id => 1, "email" => "bing#test.com"
First of all, you don't want to test the built-in conversion of json to hash. Same applies to xml.
You test controller with data as hashes, not bothering wether it's json, xml or from a html form.
But if you would like to do that as an exercise, this is a standalone ruby script to do play with :)
require 'json'
url = URI.parse('http://localhost:3030/mymodels.json')
request = Net::HTTP::Post.new(url.path)
request.content_type="application/json"
request.basic_auth('username', 'password') #if used, else comment out
hash = {:mymodel => {:name => "Test Name 1", :description => "some data for testing description"}}
request.body = hash.to_json
response = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}
puts response
to switch to xml, use content_type="text/xml" and
request.body = "<?xml version='1.0' encoding='UTF-8'?><somedata><name>Test Name 1</name><description>Some data for testing</description></somedata>"
A slightly more elegant test is to use the header helper:
header "HTTP_ACCEPT", "application/json"
json = {.... data ....}.to_json
post '/model1.json', json
Now this does exactly the same thing as setting #env; it's just a bit prettier.
The best way that I have found to test these things is with request tests. Request tests go through the full param parsing and routing stages of Rails. So I can write a test like this:
request_params = {:id => 1, :some_attribute => "some value"}
headers = {'Accept' => 'application/json', 'Content-Type' => 'application/json'}
put "/url/path", request_params.to_json, headers
expect(response).to be_success
I think that you can specify the headers with headers param:
post '/model1.json', headers: {'Content-type': 'application/json'}
Following the Rspec documentation of how provide JSON data.
#request.env["CONTENT_TYPE"] = "application/json"
OR pass in request
"CONTENT_TYPE" => "application/json"

Resources