Submitting a POST request from a rails controller - ruby-on-rails

I have followed the answer of this question. And this is what i have.
def index
$response = get_insta_feed
end
def get_insta_feed
require "net/http"
require 'net/https'
require "uri"
$tag = "test"
uri = URI.parse("https://api.instagram.com/v1/tags/"+$tag+"/media/recent")
uri.query = URI.encode_www_form(parameters)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.request_uri)
http.request(request).to_json
end
private
def parameters
{
"access_token" => "my access token here"
}
end
on my view page i just want to display the full json response first before parsing the data that i want to display so this:
<div>
<%=$response%>
</div>
and this is what is displayed in my div:
{"server":["nginx"],"date":["Fri, 07 Nov 2014 06:13:34 GMT"],"content-type":["text/html; charset=utf-8"],"allow":["GET"],"content-language":["en"],"expires":["Sat, 01 Jan 2000 00:00:00 GMT"],"vary":["Cookie, Accept-Language"],"pragma":["no-cache"],"cache-control":["private, no-cache, no-store, must-revalidate"],"set-cookie":["csrftoken=e70981e518d478dd7362049f9ce89cc9; expires=Fri, 06-Nov-2015 06:13:34 GMT; Max-Age=31449600; Path=/","ccode=PH; Path=/"],"connection":["close"]}
What am I doing wrong?

Please use instagram gem for the connection tasks to Instagram. For example you can use it as follows:
client = Instagram.client(:access_token => session[:access_token])
for media_item in client.tag_recent_media(tag_name)
# Use the folowing fields...
# media_item.images.thumbnail.url
# media_item.id
# media_item.likes[:count]
end
For more information of the gem, please refer to its github page.

Related

Passing cookies with HTTParty

I'm trying to log in a user with HTTParty into a Rails application.
The idea is to send a POST request and get a session cookie with it, then send a GET request with this cookie and log in successfully:
POST request
HTTParty.post('url/login/', basic_auth: {user: 'user', password: 'password'}, follow_redirects: false)
returns
#<HTTParty::Response:0x7f9c71bc4598 parsed_response=nil, #response=#<Net::HTTPFound 302 302 readbody=true>, #headers={"date"=>["Mon, 04 Mar 2019 08:02:26 GMT"], "server"=>["Apache"], "strict-transport-security"=>["max-age=31536000"], "set-cookie"=>["JSESSIONID=6552C1F4FD12D1C5B1D3B42168B9588A.node2; Path=/; Secure; HttpOnly"], "x-content-type-options"=>["nosniff"], "x-xss-protection"=>["1; mode=block"], "cache-control"=>["no-cache, no-store, max-age=0, must-revalidate"], "pragma"=>["no-cache"], "expires"=>["0"], "location"=>["/ipad-api/v20/login/failure/"], "vary"=>["Accept-Encoding,User-Agent"], "content-length"=>["20"], "connection"=>["close"], "content-type"=>["text/plain; charset=UTF-8"]}>
The I send a GET request
HTTParty.get('url/login/success/', cookie: "6552C1F4FD12D1C5B1D3B42168B9588A.node2")
and get
#<HTTParty::Response:0x7f9c71b95950 parsed_response={"head"=>{"apikey"=>nil, "sessionid"=>"320E4C622043566D5424627BDE11997D.node3", "timestamp"=>1551686567666, "sessiontimeout"=>1551689267666, "wishlistItemsCount"=>0, "basketItemsCount"=>0, "loggedIn"=>false, "role"=>"G"}, "data"=>{"user"=>{"profile"=>{"title"=>nil, "firstname"=>nil, "lastname"=>nil, "street"=>nil, "street2"=>nil, "postalcode"=>nil, "city"=>nil, "customerID"=>nil, "customerType"=>0}}, "abandonedBasket"=>false}, "messages"=>[{"code"=>"bmd.api.login.success", "statusCode"=>200, "description"=>"OK"}]}, #response=#<Net::HTTPOK 200 200 readbody=true>, #headers={"date"=>["Mon, 04 Mar 2019 08:02:47 GMT"], "server"=>["Apache"], "strict-transport-security"=>["max-age=31536000"], "set-cookie"=>["JSESSIONID=320E4C622043566D5424627BDE11997D.node3; Path=/; Secure; HttpOnly"], "x-content-type-options"=>["nosniff"], "x-xss-protection"=>["1; mode=block"], "cache-control"=>["no-cache, no-store, max-age=0, must-revalidate"], "pragma"=>["no-cache"], "expires"=>["0"], "vary"=>["Accept-Encoding,User-Agent"], "connection"=>["close"], "transfer-encoding"=>["chunked"], "content-type"=>["application/json;charset=UTF-8"]}>
Session changes and the user isn't logged in. Same requests with curl log a user in successfully.
Research showed that it might be not easy and this solution doesn't work either.
Any ideas what I'm doing wrong and in what direction to think? Change to faraday, as suggested here?
To login using HTTParty you have to look more things than cookies. You have to see CSRF_token too. I think you can get authenticity_token using gsub method but I tried and it was quite difficult to create regex. So I used Nokogiri to get token which is actually present in the sign in form. Following is details and at the end, I will put the whole code.
Adding required Gems, you can add it in Gemfile
gem 'httparty'
gem 'nokogiri'
Run bundle install to get gem installed.
To get CSRF_token we have to get sign_in page.
url = "http://localhost:3000/users/sign_in"
get_response = HTTParty.get(url)
noko_doc = Nokogiri::HTML(get_response)
auth_token = noko_doc.css('form').css('input[name="authenticity_token"]').first.values[2]
This way we got auth_token which was in the form as a hidden field. Now let us get cookies as session cookie may needed.
cookie_hash = HTTParty::CookieHash.new
get_response.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) }
Here we are getting cookies where session is also present.
Now it is time to get final params and than we will send both cookies and session to login
params = {"utf8" => "✓", "authenticity_token" => auth_token, "user[email]"=>"user#email.com",·
"user[password]"=>"password"}
params["commit"] = "Login"
Now params are ready, you can use following httparty request to login and get cookies.
response = HTTParty.post("http://localhost:3000/users/sign_in", {:body=>params, headers: {'Cookie' => cookie_hash.to_cookie_string }} )
Now for other request you can run same cookies method to get all cookies back
cookie_hash = HTTParty::CookieHash.new
get_response.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) }
And to access other pages you can send request with cookies as we did in above example. Remember if you again going to use any page which has form, again you need to get its csrf too.
response = HTTParty.post("http://localhost:3000/users/other_urls", {headers: {'Cookie' => cookie_hash.to_cookie_string }} )
I tried this code and it is working perfectly. Here is complete code for your use
require 'httparty'
require 'Nokogiri'
require 'Pry'
url = "http://localhost:3000/users/sign_in"
get_response = HTTParty.get(url)
noko_doc = Nokogiri::HTML(get_response)
auth_token = noko_doc.css('form').css('input[name="authenticity_token"]').first.values[2]
cookie_hash = HTTParty::CookieHash.new
get_response.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) }
params = {"utf8" => "✓", "authenticity_token" => auth_token, "user[email]"=>"user#example.com",·
"user[password]"=>"password"}
params["commit"] = "Login"
response = HTTParty.post("http://localhost:3000/users/sign_in", {:body=>params, headers: {'Cookie' => cookie_hash.to_cookie_string }} )
puts response

Convert curl (with --data-urlencode) to ruby

I am trying to convert the following curl command to ruby using net/http but I haven't figured out how to pass in the --data-urlencode script#files/jql/events.js part of the command.
curl https://mixpanel.com/api/2.0/jql -u <apikey>: --data-urlencode script#files/jql/events.js
Using net/http I had the following...
uri = URI.parse("https://mixpanel.com/api/2.0/jql")
request = Net::HTTP::Get.new(uri)
request.basic_auth("<apikey>", "")
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
Is there anyway to do this? If not within net/http then maybe using another gem?
Mixpanel has it's official ruby gem
I didn't actually work with it, but I assume it have all needed methods.
But if you don't like to use it, you may use Faraday an awesome HTTP client library for Ruby.
I made a simple example with it. Please have a look:
class MixpanelClient
def initialize(url = "https://mixpanel.com/api/2.0/jql", api_key = "ce08d087255d5ceec741819a57174ce5")
#url = url
#api_key = api_key
end
def query_data
File.read("#{Rails.root}/lib/qry.js")
end
def query_params
'{"from_date": "2016-01-01", "to_date": "2016-01-07"}'
end
def get_events
resp = Faraday.new(url: #url, ssl: { verify: false }) do |faraday|
faraday.request :url_encoded
faraday.response :logger
faraday.adapter Faraday.default_adapter
faraday.basic_auth(#api_key, "")
end.get do |req|
req.params['script'] = query_data
req.params['params'] = query_params
end
raise MixpanelError.new("Mixpanel error") unless resp.status == 200
JSON.parse(resp.body)
end
end
class MixpanelError < StandardError; end
Here is the result:
[1] pry(main)> m = MixpanelClient.new
=> #<MixpanelClient:0x007fc1442d53b8 #api_key="ce08d087255d5ceec741819a57174ce5", #url="https://mixpanel.com/api/2.0/jql">
[2] pry(main)> m.get_events
I, [2016-06-09T09:05:51.741825 #36920] INFO -- : get https://mixpanel.com/api/2.0/jql?params=%7B%22from_date%22%3A+%222016-01-01%22%2C+%22to_date%22%3A+%222016-01-07%22%7D&script=function+main%28%29%7B+return+Events%28params%29.groupBy%28%5B%22name%22%5D%2C+mixpanel.reducer.count%28%29%29+%7D
D, [2016-06-09T09:05:51.741912 #36920] DEBUG -- request: Authorization: "Basic Y2UwOGQwODcyNTVkNWNlZWM3NDE4MTlhNTcxNzRjZTU6"
User-Agent: "Faraday v0.9.2"
I, [2016-06-09T09:05:52.773172 #36920] INFO -- Status: 200
D, [2016-06-09T09:05:52.773245 #36920] DEBUG -- response: server: "nginx/1.9.12"
date: "Thu, 09 Jun 2016 03:05:52 GMT"
content-type: "application/json"
transfer-encoding: "chunked"
connection: "close"
vary: "Accept-Encoding"
cache-control: "no-cache, no-store"
access-control-allow-methods: "GET, POST, OPTIONS"
access-control-allow-headers: "X-PINGOTHER,Content-Type,MaxDataServiceVersion,DataServiceVersion,Authorization,X-Requested-With,If-Modified-Since"
=> [{"key"=>["Change Plan"], "value"=>186}, {"key"=>["View Blog"], "value"=>278}, {"key"=>["View Landing Page"], "value"=>1088}, {"key"=>["login"], "value"=>1241}, {"key"=>["purchase"], "value"=>359}, {"key"=>["signup"], "value"=>116}]
A set ssl: {verufy: false} because Faraday need addtitional workaround to work with ssl certificates: https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates

How to access data requested by Post method in rails

Am trying to access data from a certain API using POST method but it returns back the actual list of params that I sent. Here is my code I don't know whether am doing this right, I will be glad for your help.
This is my controller
#Request access token from ExactApi
params = {
"code" => "#{code}",
"redirect_uri" => '/auth/exact/callback',
"grant_type" => "authorization_code",
"client_id" => "{CLIENT_ID}",
"client_secret" => "CLIENT_SECRET"
}
uri = URI.parse('https://start.exactonline.nl/api/oauth2/token')
#Encode the url into /x-www-form-urlencoded
uri.query = URI.encode_www_form(params)
#Transform http protocol into a secure protocol[https]
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
#Send the request to the ExactApi and return the received data
res = Net::HTTP::Post.new(uri.request_uri)
res.set_form_data(params)
puts "Received:: "+ res.body.to_yaml
Output
code[CODE]&redirect_uri=%2Fauth%2Fexact%2Fcallback&grant_type=authorization_code&client_id=CLIENT_ID&client_secret=SECRET_ID
How can I access the actual data returned from API?
require "net/http"
require "uri"
uri = URI.parse('https://start.exactonline.nl/api/oauth2/token')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
request.set_form_data({
"code" => "#{code}",
"redirect_uri" => '/auth/exact/callback',
"grant_type" => "authorization_code",
"client_id" => CLIENT_ID,
"client_secret" => CLIENT_SECRET
})
response = http.request(request)
http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
However I would use Omniauth instead of reinventing the Oauth wheel. Its pretty hard to get right. If you cannot find a ready made provider then creating a custom provider is pretty simple:
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class ExactOnline < OmniAuth::Strategies::OAuth2
# change the class name and the :name option to match your application name
option :name, :exactonile
option :client_options, {
:site => "https://start.exactonline.nl",
:authorize_url => "/api/oauth2/token"
}
uid { raw_info["id"] }
info do
{
:email => raw_info["email"]
# and anything else you want to return to your API consumers
}
end
def raw_info
#raw_info ||= access_token.get('/api/v1/me.json').parsed
end
end
end
end
You are using puts which outputs to the server console. I'm confused where the output is. You should set yourself up a view with the same name as the controller action your block of code is within, for example if this is the index action:
def index
params= { your_hash_keys: "value" }
end
Then you should have an index.html.erb inside the app/views/controller_name/ In your controller instead of puts "Received:: "+ res.body.to_yaml use #debug = "Received:: "+ res.body.to_yaml and inside your view do something to output it like <%= #debug.inspect %>
Alternatively, and not recommended is to render inline in the controller:
render inline: "Received:: " + res.body.to_yaml
Layouts and Rendering with inline
You also should rename your params variable, since that is used by Rails for the incoming parameters. All in all I think a tutorial on MVC would be a good place to start.

check https status code ruby

Is there a way to check for an HTTPS status code in ruby? I know that there are ways to do this in HTTP using require 'net/http', but I'm looking for HTTPS. Maybe there is a different library that I need to use?
You can do this in net/http:
require "net/https"
require "uri"
uri = URI.parse("https://www.secure.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri)
res = http.request(request)
res.code #=> "200"
Refs:
Net::HTTP cheat sheet
How to Cure Net::HTTP’s Risky Default HTTPS Behavior
You can use any wrapper around Net::HTTP(S) to get much easier behavior.
I use Faraday here ( https://github.com/lostisland/faraday ) but HTTParty has almost the same functionality ( https://github.com/jnunemaker/httparty )
require 'faraday'
res = Faraday.get("https://www.example.com/")
res.status # => 200
res = Faraday.get("http://www.example.com/")
res.status # => 200
(as a bonus you get options for parsing responses, raising state exceptions, logging requests....
connection = Faraday.new("https://www.example.com/") do |conn|
# url-encode the body if given as a hash
conn.request :url_encoded
# add an authorization header
conn.request :oauth2, 'TOKEN'
# use JSON to convert the response into a hash
conn.response :json, :content_type => /\bjson$/
# ...
conn.adapter Faraday.default_adapter
end
connection.get("/")
# GET https://www.example.com/some/path?query=string
connection.get("/some/path", :query => "string")
# POST, PUT, DELETE, PATCH....
connection.post("/some/other/path", :these => "fields", :will => "be converted to a request string in the body"}
# add any number of headers. in this example "Accept-Language: en-US"
connection.get("/some/path", nil, :accept_language => "en-US")
require 'uri'
require 'net/http'
res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
puts res.code # -> '200'
Slightly more readable way:
response.kind_of?(Net::HTTPOK)

How do I touch a URL with rails?

I have a json URL supplied by sendgrid. All it needs to be is touched. How would I do this?
def suspend
#user = User.find(params[:id])
#user.update_attribute("suspended", true)
# the url I need to touch => https://sendgrid.com/api/unsubscribes.add.xml?api_user=username%40website.com&api_key=secret_password&email=#{#user.email}
end
You can use Net::HTTP.get from standard library (see docs):
require 'net/http'
Net::HTTP.get URI("https://sendgrid.com/api/unsubscribes.add.xml?api_user=username%40website.com&api_key=secret_password&email=#{#user.email}")
Updated:
For HTTPS you can do smth like that:
require "net/https"
uri = URI.parse("https://www.google.com")
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)
# request = Net::HTTP::Head.new(uri.request_uri) - get response without body
response = http.request(request)
Nice article on the subject - Ruby Net::HTTP Cheat Sheet.
Install httpclient gem
HTTPClient.get("https://sendgrid.com/api/unsubscribes.add.xml?api_user=username%40website.com&api_key=secret_password&email=#{#user.email}")
maybe try ActionDispatch GET http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-GET
require 'open-uri'
open("http://pragprog.com/") { |f| f.gets }
result? just one row, not the whole page:
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"

Resources