Rails reading API response header - ruby-on-rails

I am getting familiarised with APIs. As a start, I am using the Forecast API.
In the docs, you will find a section entitled "Response Headers". What are they, and how can I use them?
Also, to get a response, it says you need to pass an API key, along with lat and long data. But aren't API keys supposed to be kept secret? Will anyone find out the contents of the request?
This is the code I have:
Forecast model
require 'json'
class Forecast
include HTTParty
debug_output $stdout
default_params :apiKey => 'xxxxxxxxxxxxxxxxxxxxxxxxx'
base_uri "api.forecast.io"
format :json
def self.get_weather(api,lat,long)
#response = get("/forecast/#{apiKey}/#{lat},#{long}")
end
def self.show_weather
JSON.parse(#response.body)
end
end
Forecast controller
def index
#weather = Forecast.get_weather("28.5355", "77.3910")
#response = Forecast.show_weather
end
Forecast view
<%= #response["currently"]["summary"] %>

You're asking a couple of different questions here.
Response headers: They are part of an HTTP response, and contain information about the response. For example, they might tell you the MIME-type of the response - eg. Content-Type: application/json. In this case, Forecast use it to tell you how many API calls you've made (X-Forecast-API-Calls) and how long it took them to respond (X-Response-Time) as well as some caching information.
API keys: Yes, these should be kept secret. The Forecast API works over HTTPS, so (in theory) your API key should be kept secret from people sniffing traffic on your network. The main danger is keeping it in your code and, for example, committing it to GitHub. You should figure out a safer way to store the API key. One example, whilst not perfect, would be to have it as an environment variable.
I hope that helps.

Related

Verify Shopify webhook

I believe that to have a Shopify webhook integrate with a Rails app, the Rails app needs to disable the default verify_authenticity_token method, and implement its own authentication using the X_SHOPIFY_HMAC_SHA256 header. The Shopify docs say to just use request.body.read. So, I did that:
def create
verify_webhook(request)
# Send back a 200 OK response
head :ok
end
def verify_webhook(request)
header_hmac = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
digest = OpenSSL::Digest.new("sha256")
request.body.rewind
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, SHARED_SECRET, request.body.read)).strip
puts "header hmac: #{header_hmac}"
puts "calculated hmac: #{calculated_hmac}"
puts "Verified:#{ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, header_hmac)}"
end
The Shopify webhook is directed to the correct URL and the route gives it to the controller method shown above. But when I send a test notification, the output is not right. The two HMACs are not equal, and so it is not verified. I am fairly sure that the problem is that Shopify is using the entire request as their seed for the authentication hash, not just the POST contents. So, I need the original, untouched HTTP request, unless I am mistaken.
This question seemed like the only promising thing on the Internet after at least an hour of searching. It was exactly what I was asking and it had an accepted answer with 30 upvotes. But his answer... is absurd. It spits out an unintelligible, garbled mess of all kinds of things. Am I missing something glaring?
Furthermore, this article seemed to suggest that what I am looking for is not possible. It seems that Rails is never given the unadulterated request, but it is split into disparate parts by Rack, before it ever gets to Rails. If so, I guess I could maybe attempt to reassemble it, but I would have to even get the order of the headers correct for a hash to work, so I can't imagine that would be possible.
I guess my main question is, am I totally screwed?
The problem was in my SHARED_SECRET. I assumed this was the API secret key, because a few days ago it was called the shared secret in the Shopify admin page. But now I see a tiny paragraph at the bottom of the notifications page that says,
All your webhooks will be signed with ---MY_REAL_SHARED_SECRET--- so
you can verify their integrity.
This is the secret I need to use to verify the webhooks. Why there are two of them, I have no idea.
Have you tried doing it in the order they show in their guides? They have a working sample for ruby.
def create
request.body.rewind
data = request.body.read
header = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
verified = verify_webhook(data, header)
head :ok
end
They say in their guides:
Each Webhook request includes a X-Shopify-Hmac-SHA256 header which is
generated using the app's shared secret, along with the data sent in
the request.
the keywors being "generated using shared secret AND DATA sent in the request" so all of this should be available on your end, both the DATA and the shared secret.

Streaming API with Rails not able to get streamed data

I am trying to fetch data from Twitter Streaming API in my rails app and i have created my own module which gives me twitter Authorization Head. I am able to get authorized but i am not getting the response back... all i see is the request in pending state (i am guessing as its streaming and connection not being closed). What can i do to my below code so that i can start printing the response as i get from Streaming API ?
class MainController < ApplicationController
include Oauth::Keys
def show
#oauth_signature_string = Oauth::Signature.generate(signature_params)
#header = Oauth::HeaderString.create(header_params)
RestClient::Request.execute(method: :GET, url: 'https://stream.twitter.com/1.1/statuses/sample.json', :headers => {:Authorization => %Q(OAuth ****************************)} )
end
end
Because Twitter is "streaming" the data and not closing the connection, your RestClient request is not being ended and your show action is hanging there and not "finishing". So, rails can't continue and render the default main/show.html.erb page.
So, you might want to look into ActionController::Streaming class and see if you can rewrite you views and HTTP call to utilize it. Or, it would be much easier to use a non-streaming API edge.
Also, what you are doing seems to be a better fit for javascript. You might want to use Twitter's official Javascript api to do all authentications and status streams.

Is it possible to request partial models with ActiveResource?

We have a suite of applications that make heavy use of ActiveResource to provide data across the system. We also override model#as_json a lot to provide additional 'readonly' data for use by the client. This additional data is provided on every resource request and is creating performance issues, especially if we only want basic subset of the model data. Is it possible to request 'variants' of the return data (similar to how HTTP allows for basic info via HEAD requests, and whole response via GET requests)?
# Full response in JSON
def as_json(opts)
super(opts).merge({
"connected_person_name" => self.connected_person.name
})
end
# A Partial response in JSON
def basic_as_json(opts)
super(opts).slice("id", "name", …)
end
Ideally, I'd like to be able to have a controller respond_to { |f| … } style block choosing which JSON method to call based on some parameter of the request. I could include this an attribute, but as it's configuration for the request, and not a model attribute, this seems like the wrong place to put it. I feels like this should be part of the request header, but i haven't seen anything that covers my needs.

Save a response from API call to use in a test so I don't have to continuously repeat requests to API

API requests take too long and are costing me money in my Rails integration tests and my application.
I would like to save API responses and then use that data for testing. Are there any good ways to make that happen?
Also, how can I make fewer api calls in production/development? What kind of caching can I use?
If I understand correctly, your rails app is using an external api, like a google/fb/twitter api, this kind of stuff
Caching the views won't work, because it only caches the template, so it doesn't waste time rendering the view again, and it validates that the cache is warm by hashing the data, which the code will still hit the api to verify that the hashes still match
For you the best way is to use a class that does all the api calls, and cache them in rails cache and give that cache a timeout period, because you don't want your cache to be too stale, but in the same time you will sacrifice some accuracy for some money ( like only do a single call every 5, 15, 30 mins, which ever you pick )
Here's a sample of what I have in mind, but you should modify it to match your needs
module ApiWrapper
class << self
def some_method(some_key) # if keys are needed, like an id or something
Rails.cache.fetch("some_method/#{some_key}", expires_in: 5.minutes) do
# assuming ApiLibrary is the external library handler
ApiLibrary.call_external_library(some_key)
end
end
end
end
Then in your code, call that wrapper, it will only contact the external api if the stored value in the cache is expired.
The call will be something like this
# assuming 5 is the id or value you want to fetch from the api
ApiWrapper.some_method(5)
You can read more about caching methods from the rails guide for caching
Update:
I just thought of another way, for your testing (like rspec tests) you could stub the api calls, and this way you'll save the whole api call, unless you are testing the api it self, using to the same api library I wrote above, we can stub the ApiLibrary it self
allow(ApiLibrary).to receive(:some_method).and_return({ data: 'some fake data' })
PS: the hash key data is part of the return, it's the whole hash not just the string.
There is a great gem for this called VCR. It allows you to make a single request and keep response cached, so every time you run the test you will use this saved response.
I would use http://redis.io/ in conjunction with something like jbuilder. So as an example your view would look like:
json.cache! ["cache", "plans_index"] do
json.array! #plans do |plan|
json.partial! plan
end
end
for this controller:
def index
#plans = Plan.all
end
If you have something that is a show page you can cache it like this:
json.cache! ["cache", "plan_#{params["id"]}"] do
json.extract! #plan, :short_description, :long_description,
end

Rails: Read the contents from another site

In Rails, how can I make an http request to a page, like "http://google.com" and set the response to a variable?
Basically I'm trying to get the contents of a CSV file off of Amazon S3:
https://s3.amazonaws.com/datasets.graf.ly/24.csv
My Rails server needs to return that content as a response to an AJAX request.
Get S3 bucket
Access the file and read it
Render its contents (so the ajax request receives it)
A few questions have suggested screen scraping, but this sounds like overkill (and probably slow) for simply taking a response and pretty much just passing it along.
API
Firstly, you need to know how you're accessing the data
The problems you've cited are only valid if you just access someone's site through HTTP (with something like CURL). As you instinctively know, this is highly inefficient & will likely get your IP blocked for continuous access
A far better way to access data (from any reputable service) is to use their API. This is as true of S3 as Twitter, Facebook, Dropbox, etc:
AWS-SDK
#GemFile
gem "aws-sdk-core", "~> 2.0.0.rc2"
#config/application.rb
Aws.config = {
access_key_id: '...',
secret_access_key: '...',
region: 'us-west-2'
}
#config/initializers/s3.rb
S3 = Aws::S3.new
S3 = Aws.s3
Then you'll be able to use the API resources to help retrieve objects:
#controller
# yields once per response, even works with non-paged requests
s3.list_objects(bucket:'aws-sdk').each do |resp|
puts resp.contents.map(&:key)
end
CORS
If you were thinking of xhring into a server, you need to ensure you have the correct CORS permissions to do so
Considering you're wanting to use S3, I would look at this documentation to ensure you set the permissions correctly. This does not apply to the API or an HTTP request (only Ajax)
To do as you asked:
the open-uri solution from
How make a HTTP request using Ruby on Rails?
(to read from https in the simplest way possible), and
the set headers solution from in rails, how to return records as a csv file,
and
and a jquery library to decode the csv, eg http://code.google.com/p/jquery-csv/ -
Alternatively decode the csv file in rails and pass a json array of arrays back:
decode the csv as suggested in Rails upload CSV file with header
return the decoded data with the appropriate type
off the top of my head it should be something like:
def get_csv
url = 'http://s3.amazonaws.com/datasets.graf.ly/%d.csv' % params[:id].to_i
data = open(url).read
# set header here
render :text => data
end

Resources