How do I refactor an API call to use HTTParty? - ruby-on-rails

I have a working API call to Twitter. Yay me! But the problem is is that I would much rather refactor it into HTTParty and then I can extend it later. There are an assortment of reasons as to why I am not using something like the twitter gem. They are mainly due to some limitations that need to be overcome for the application.
Here, I have a working piece of code that calls to Twitter:
class Twitter
def validate
consumer_key = OAuth::Consumer.new(
ENV['TWITTER_CONSUMER_KEY'],
ENV['TWITTER_CONSUMER_SECRET']
)
access_token = OAuth::Token.new(
ENV['TWITTER_ACCESS_TOKEN'],
ENV['TWITTER_ACCESS_SECRET']
)
baseurl = "https://api.twitter.com"
address = URI "#{baseurl}/1.1/account/verify_credentials.json"
http = Net::HTTP.new address.host, address.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new address.request_uri
request.oauth! http, consumer_key, access_token
http.start
response = http.request request
puts "The response status was #{response.code}"
end
end
It is dependent on only the oath gem.
Key Question: How would one wrap this into HTTParty to make it more modular?

You could just replace NET::HTTP with HTTParty to get the benefits of the latter, or you could go the extra mile and make your Twitter model include HTTParty so that it responds to an ActiveRecord-like interface while it abstracts that in the background is issuing all these API requests.
The decision really depends on your needs. Do you just need to issue a specific request to Twitter and display the results or you want to interact more heavily with Twitter and treat it as a model where you can create, retrieve, delete etc.
Regardless of your choice, I believe that the official readme has all the information you might need (it even has a great example with StackExchange!).

Related

How to set custom header for all requests that go through OmniAuth?

This is mostly a thought exercise that I couldn't figure out how to solve. :)
Does anyone know how to set a custom request header for all requests that use the omniauth or omniauth-oauth2 gems?
I've tried something like the following, but I don't see the header in my developer tools in Chrome, for example.
OmniAuth.config.before_request_phase do |env|
env['MY_CUSTOM_HEADER'] = 'true'
end
What am I doing wrong here? What am I missing here?
Edit based on responses:
Why I need the custom header set doesn't really matter here -- as I mentioned at the top of the question: this is just a curiosity I had. The constraint is that I'd like to test the custom header on any OS or browser, so it's not sufficient to just play with developer tools in Chrome here as not all browsers have that capability on across all OSes.
What I'm trying to do is add a custom header to all outgoing OAuth requests coming out the gem. That's it.
In browser developer tools you can only see request headers that are sent by or response headers received by the browser. Any request headers set by load balancers, reverse proxies, middleware and so on - are only visible to next stage in request handling chain.
OmniAuth lives in middleware, so technically any request in your app uses it, unless some other middleware terminates request chain and renders some response.
Also "request" in omniauth terms is not http request, it's an auth phase, usually happens on /auth/:provider (also there're "options" and "callback" phases).
It's not clear why you need setting a request header in a middleware on a request that goes into your own application - when request is going to be handled by omniauth it will not hit your controllers, except for callback phase where request.env['omniauth.auth'] is going to be set.
Since you're mentioned developer tools - probably you want response headers on request phase, to set these you need to override rack response returned from request_phase in your strategy. But for oauth2 there's only a redirect (still possible to set headers, but even less sense).
So first you need to be exact on which headers you want to be set at which request/response, there're several of those. Simplified sequence:
An OAuth request handler cannot force a browser (or any similar user-agent) to disclose more information than specified in the HTTP protocol. Be glad of that: any other posture could lead to information leakage.
The only connection that Omniauth might make itself is that exchanging a code for an access/refresh token. That is specific to the strategy in question, but strategies have the opportunity to include arbitrary headers in their internal client. If you're writing a custom strategy that required a basic authentication header during the access token exchange, it might look like this:
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class DemoStrategy < OmniAuth::Strategies::OAuth2
option :name, "demo"
option :client_options, {
site: 'https://api.example.org',
authorize_url: 'https://auth.example.org/oauth2/authorize',
token_url: 'https://auth.example.org/oauth2/token'
}
uid { raw_info['id'].to_s }
info do
{ email: raw_info['email'], image: raw_info['avatar_url'] }
end
extra do
{ raw_info: raw_info }
end
def raw_info
#raw_info ||= access_token.get('user').parsed
end
def build_access_token
options.token_params.merge!(headers: {
'Authorization' => special_auth_header
})
super
end
def basic_auth_header
"Basic " + Base64.strict_encode64("#{options[:demo_id]}:#{options[:demo_secret]}")
end
end
end
end
Here, build_access_token is overriding the superclass's standard constructor for the internal HTTP client, and injecting extra headers before handing it back up the stack. Internally that's handed off to the oauth2 gem, which in turn uses Faraday, so it's likely anything Faraday accepts is a valid option.
If you need additional information carried to the authentication server, it may be encoded in the redirect URL by the strategy. For example, the omniauth-google-oauth2 strategy is configurable to carry authentication scopes and email hints in the URL that lands on Google's authentication endpoint.
It is also common to include a XSRF state parameter, in conjunction with an encrypted session cookie, to protect against identity spoofing. Depending on the co-operation of the authentication server, some or all of this data may be reflected in the redirection back to your handler.
At it simplest, that is handled by the authorize_params method in the strategy subclass e.g.
def authorize_params
super.tap do |params|
params[:something] = 'my_extra_value'
end
end
However, the volume of code involved in setting up extended parameters may be quite substantial in practice. For a worked example of doing this with Omniauth I'd suggest taking a looking at the source code of the Google strategy, and again I'll draw your attention to the authorize_params method which is the entry point for this heavy lifting.
In the overall flow of things, those are the touch points where server-side code can actually influence matters. There's a fundamental expectation that the user's client/browser is participating by executing nothing but normal HTTPS request.

Sending POST requests from Rails controller for authentication

I am quite new to Ruby and RoR, and have been struggling with a problem and am not getting anywhere.
Basically I am building a "proxy" webservice that will handle certain requests, passing them to a third party website. The response received from the third party website is in HTML, which will then be parsed and an appropriate XML response will be given.
I'm at a point where I need to send POST requests via my controller in Rails to authenticate a user, which I'm doing using this code:
require "net/http"
require "uri"
uri = URI.parse("http://myurl.com/members.cgi")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"email_login" => "user#email.com", "password_login" => "password"})
response = http.request(request)
My problem lies on the response I am receiving. This specific POST request, when successful (i.e. user has successfully authenticated), is returning a 302 redirect. That in itself is not an issue, as I can follow the redirect by getting the 'location' header value:
redirectLocation = response['location']
Where I'm getting stuck is in keeping myself authenticated when following the redirect with the following line:
redirectResponse = Net::HTTP.get_response(URI.parse(redirectLocation))
This follows the redirect but return a response showing I am not authenticated??
I don't really understanding why this is happening. I can see there is an authentication cookie being returned with my original response by reading:
response['cookie']
So finally my question is what do I need to do to get the server to recognise my authentication status? Pass the cookie with my second request somehow? If so, how do I do it?
Many thanks in advance for your time!
Rog
Yes you need to set the cookie. I think it probably give you some kind of a session id. Which you need to pass with every request.
look at this code snippet for a example on how to pass on a cookie that you get as a response with your new requests.

Connecting to web services using Rails (HTTP requests)?

I am using Ruby on Rails 3 and I am trying to implement APIs to retrieve account information from a web service. That is, I would like to connect to a web service that has the Account class and get information from the show action routed at the URI http://<site_name>/accounts/1.
At this time, in the web service accounts_controller.rb file I have:
class AccountsController < ApplicationController
def show
#account = Account.find(params[:id])
respond_to do |format|
format.html
format.js
format.json { render :json => #account.to_json }
end
end
end
Now I need some advice for connecting to the web service. In the client application, I should have a HTTP GET request, but here is my question: What is "the best" approach to connect to a web service making HTTP requests?
This code in the client application works:
url = URI.parse('http://<site_name>/accounts/1.json')
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
#output = JSON(res.body)["account"]
but, is the above code "the way" to implement APIs?
Is it advisable to use third-party plugins and gems?
Yes, since you're using RESTful routes, they are your basic API. You're also returning structured JSON data easily consumable by an application.
There are other ways to implement a web services API (e.g. SOAP), but this is a good and proper way.
Since it's a web services API, connecting to the correct URL and parsing the response is the way to go on the client side. Though if you need to access many different resources it might be a good idea to create a flexible way of building the request URL.
If you don't need low-level tweak-ability offered by Net::HTTP, instead take a look at using Open-URI, which comes with Ruby. It makes it easy to request a page and receive the body back. Open-URI doesn't have all the bells and whistles but for a lot of what I do it's plenty good.
A simple use looks like:
require 'open-uri'
body = open('http://www.example.com').read
The docs have many other examples.
These are other HTTP clients I like:
HTTPClient
Typhoeus
They are more tweakable and can handle multiple connections at once if that's what you need. For instance, Typhoeus has a suite of simplified calls, similar to Open-URI's. From the docs:
response = Typhoeus::Request.get("http://www.pauldix.net")
response = Typhoeus::Request.head("http://www.pauldix.net")
response = Typhoeus::Request.put("http://localhost:3000/posts/1", :body => "whoo, a body")
response = Typhoeus::Request.post("http://localhost:3000/posts", :params => {:title => "test post", :content => "this is my test"})
response = Typhoeus::Request.delete("http://localhost:3000/posts/1")
HTTPClient has similar shortened methods too.
I'd recommend using Rails' ActiveResource for most cases. Failing that, httparty.
I would use ActiveResource if you just need a simple way to pull in rest-based resources. It's already included in rails and pretty trivial to set up. You just specify a base url and resource name in your ActiveResource subclass and then you can CRUD rest-based resources with an interface similar to that of ActiveRecord.
rest-client is the most popular gem for easily connecting to RESTful web services
I think one of the best solutions out there is the Her gem. It encapsulates restful requests providing objects with active-record like behavior.

Access URL on another website from a model in rails

I want to access a URL of another website from one of my models, parse some information and send it back to my user. Is this possible?
For example, the user sends me an address through a POST, and I want to validate the information through a third party website (USPS or GMaps)
What methods would I use to create the request and parse the response?
This is not a redirect. I want to open a new request that is transparent from the client.
There are a lot of libraries to handle this such as:
HTTParty on http://github.com/jnunemaker/httparty
Curb on http://curb.rubyforge.org/
Patron on http://github.com/toland/patron
Example using Patron:
sess = Patron::Session.new
sess.timeout = 10
sess.base_url = "http://myserver.com:9900"
sess.headers['User-Agent'] = 'myapp/1.0'
resp = sess.get("/foo/bar")
if resp.status < 400
puts resp.body
end
Each solution has its own way of handling requests and parsing them as well as variations in their API. Look for what fits your needs the best.

rails activeresource messages

I have a quick question about active resource. If I have a User resource, when I do a call like
User.find(1).put(:promote, :position => 'manager')
According to the api it translates into this call /users/1/promote.xml?position=manager
My question is this. Is activeresource actually making two calls here? find doing a get, then putting on that object, or does appending .put to the .find mean that it just makes a single call. If this is so, then the only reason for the .find is the give the proper url format of /users/:id/promote ??
I couldn't find in the docs where this might be specified, but it's the .find that makes me think maybe two service calls are taking place?
If ActiveResource works like ActiveRecord, I would say 'yes'. If you do something like
Foo.find(1).update_attributes(:name=>"Bar")
ActiveRecord first does a select to get the object and then issues an update call to the database to change the record. I would presume that ActiveResources functions in a similar manner where it issues two web services calls to get the object and then update the object.
Put the following in your intializers:
class ActiveResource::Connection
# Creates new Net::HTTP instance for communication with
# remote service and resources.
def http
http = Net::HTTP.new(#site.host, #site.port)
http.use_ssl = #site.is_a?(URI::HTTPS)
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
http.read_timeout = #timeout if #timeout
#Here's the addition that allows you to see the output
http.set_debug_output $stderr
return http
end
end
Search '"http.set_debug_output $stderr" "ActiveResource"' to find the original author of this helpful tip.

Resources