Parsing Data from httpparty response - ruby-on-rails

I am returning quite a lot of JSON from a http party request
In my Rails controller I got this data:
#data = #request.parsed_response["InfoResponses"]
and in my html.erb I displayed the #data on screen and it is as below:
[{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>BLUE}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>17}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"2"}, "Id"=>"2", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"2"}},
{"AdditionalResponse"=>nil, "AddressResponse"=>nil, "HighResResponse"=>nil, "LowResResponse"=>nil, "OtherResponse"=>nil, "Error"=>nil, "CarResponse"=>{"Values"=>[{"FieldDescription"=>"Colour", "FieldName"=>"COLOUR", "Value"=>RED}, {"FieldDescription"=>"Engine Size", "FieldName"=>"ENGINESIZE", "Value"=>1400}, {"FieldDescription"=>"Number Doors", "FieldName"=>"NUMBERDOORS", "Value"=>4}, {"FieldDescription"=>"Fuel", "FieldName"=>"FUEL", "Value"=>GAS}, {"FieldDescription"=>"Wheel Size", "FieldName"=>"WHEELSIZE", "Value"=>19}, {"FieldDescription"=>"CD Player", "FieldName"=>"CDPLAYER", "Value"=>Y}],
"Error"=>nil, "Id"=>"1"}, "Id"=>"1", "DensityResponse"=>nil, "RiskIndexResponse"=>nil, "FinanceResponse"=>{"Value"=>Available, "Error"=>nil, "Id"=>"1"}}]
On screen I only want to Display the CarResponse Details - i.e all the Fields such as Colout, EngineSize, etc and their values and the only other details I want to extract are the FinanceResponse the Value and the Id.
What is the best way to extract these details - I have shown 2 here but my JSON response could include the same response as above repeated.
EDIT - My HttpParty code is as below
def self.GetInforFromASPWebAPI
#request_body = [{:Id => '1',
:A => '1',
:B => '1',
:C=> '1'}]
post('localhost:50544/api/MyController/GetInfo', :body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'})
end
This is in a Helper class - then in my controller I was doing something like the below:
#response = ASPWebAPIClass.GetInforFromASPWebAPI
The contents of the response was even more data so I used what I had done before to strip some of it down to the parsed_response.

You should have a full class representing the API. Your methods would then get what you're interested in, rather than passing an entire response back to the Rails controller forcing the controller to know how to parse the API's response. Put all the API knowledge into the API class in accordance with the Single Responsibility Principle. Your code will be cleaner as a result.
Example:
class CarAPI
include HTTParty
base_uri 'localhost:50544/api/MyController'
# any other initialization stuff
def initialize
#options = {
:body => #request_body.to_json,
:headers => {'Content-Type' => 'application/json'}
}
end
def car_info
response = self.class.post '/getinfo'
# Create a simple Ruby hash with key/value pairs
cars = {}
response['InfoResponses'][0]['CarResponses']['Values'].each do |field|
cars[field['FieldName']] = field['Value']
end
cars
end
end
This is a bit simplified; you'll want to iterate over all the responses (replace the [0] with a loop/map).

Related

stub_request when request body is not predictable

I am stubbing an http request with stub_request. This http request is basically a slack notification, that contains some random string (e.g. a timestamp.)
So, I can not just reuse the snippet, rspec spits out to me, because body differs on every execution. Is there any possibility to stub a request with, say, pattern, or I am stuck to hook e.g. the Slack#ping?
The dried code, jic:
mutation
class MyMutation < Mutations::Command
def run
slack.ping "#{rand (1..1000)}"
end
end
spec
describe MyMutation do
# ??? stub_request ???
it 'succeeded' do
expect(MyMutation.new.run.outcome).to be_success
end
end
Thanks.
UPD stub request:
stub_request(:post, "https://hooks.slack.com/services/SECRETS").
with(:body => {"payload"=>"{SLACK_RELATED_PROPS,\"text\":\"MY_RANDOM_HERE\"}"},
:headers => {'Accept'=>'*/*', MORE_HEADERS}).
to_return(:status => 200, :body => "", :headers => {})
You need to use partial hash matching:
stub_request(:post, "https://hooks.slack.com/services/SECRETS").
with(:body => hash_including("payload"=>"{SLACK_RELATED_PROPS}"),
:headers => {'Accept'=>'*/*', MORE_HEADERS}).
to_return(:status => 200, :body => "", :headers => {})
I'd also recommend to provide SLACK_RELATED_PROPS as a hash, not as a json-encoded string. Just choose some values from there that you really care about, and strip everything else, like your random-generated value.
You can view more features in docs, such as regex matching or even dynamic evaluating on request object.

Failing to parse JSON in Rails

I am trying to have one of my pages request and display info from the last.fm API. At this point there is no user input and I just want the root page on my application to display the results of this API call.
My last_fm Controller:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:api_key => 'xxxxxyyyyyyzzzzz'})
end
def index
#events = LastFmController.get_events
end
end
My last_fm#index view is empty except this: <%= #events %>.
The error I'm getting when I go to the last_fm#index view:
795: unexpected token at '<?xml version="1.0" encoding="utf-8"?>
<lfm status="ok">
<events xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" location="San Francisco, United States" page="1" perPage="10" totalPages="37" total="364" festivalsonly="0" tag="">
<event xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" >
<id>3782989</id>
<title>Broken Hope</title>
<artists>
<artist>Broken Hope</artist>
<artist>Oceano</artist>
This trace actually includes the entire hash I received from the API call, so it's long and I'm omitting it for brevity.
The title of the error page is Action Controller: Exception caught.
I would be thankful if I could just do some basic manipulation with the hash I received, like being able to navigate the hash and only display certain items. Thanks!
You are trying to parse XML as JSON. XML parser here: Nokogiri
LastFM returns XML by default. You have to pass in format=json as a paramter so it returns JSON. This should work:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:format => 'json',
:api_key => 'xxxxyyyyyzzzz'})
end
def index
#events = LastFmController.get_events
end
end

Rails and XML requests

In my Rails 4 app I am dealing with an API that only handles XML (yes I wish it was JSON too).
I have to make a POST request and the XML string should be stored in a parameter called xmlRequestString.
The XML structure for the POST data is:
<?xml version="1.0" encoding="UTF-8"?>
<GetProperties>
<Auth>
<VendorId>UserID</ VendorId>
<VendorPassword>Password</VendorPassword>
</Auth>
</GetProperties>
As I have never even touched XML before could someone show me how I would actually post this data.
Would something like this be a good way of going about it (borrowed from here: Submitting POST data from the controller in rails to another website)?
require "uri"
require "net/http"
xml = 'xml string can go here'
params = {'xmlRequestString' => xml}
Net::HTTP.post_form(URI.parse('urlendpoint'),params)
You can save this as a template, with instance variables like in a regular html.erb template. Or, you could have it as a method in a model. Either way, you're using something that takes some dynamic data and returns you a text string, that has the xml in it. Then, in your controller, render out the template, or call the method (if you put it in a model) and post it to the api.
#Template method of generating xml
#app/views/properties/get_properties.rxml
xml.instruct! :xml, :version=>"1.0", :encoding => "UTF-8"
xml.GetProperties do
xml.Auth do
xml.VendorId do
<%= #user_id %>
end
xml.VendorPassword do
<%= #password %>
end
end
end
Then, in a controller, call the api:
#user_id = "foo"
#password = "bar"
xml_string = render :template => "properties/get_properties.rxml", :layout => false
http = Net::HTTP.new("www.the-api-website.com")
response = http.post("/path/to/call/the/api", xml_string)

Post destination receive put data 2 times in parameters

I'm developping a post to a callback url in Ruby on Rails and use the Httparty library for this, I receive the post perfectly on the url but it seems that rails convert the data that is pushed to the url 2 times to parameters. Here is the code that I use to do the call :
#result = HTTParty.post("http://localhost:3000/mailchimp/callback/",
:body => {
:data => {
:title => 'This is the screen name'}
}.to_json,
:headers => { 'Content-Type' => 'application/json' } )
In the logs of the receiving application I got this :
Parameters: {"mailchimp"=>{"controller"=>"mailchimp", "action"=>"callback", "data"=>{"title"=>"This is the screen name"}}, "data"=>{"title"=>"This is the screen name"}}
You see directly that I have 2 times the data parameters, once in the controller hash and once in the normal parameters hash. How does this come?
This is caused by the ParamsWrapper module https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/params_wrapper.rb
This is enabled by default in your rails app by the initializer config/wrap_parameters.rb

How do I pass this param properly?

I want to make an API request with a text param, with information I currently have in params[:brand][:tag_list] which seems to be saved as a single comma-delimited string. What's the correct way to make the API request?
Controller code:
current_user.tag(#brand, :with => params[:brand][:tag_list], :on => :tags)
url = "http://www.viralheat.com/api/sentiment/review.json"
#sentiment_response = url.to_uri.get(
:api_key => 'MY_KEY',
:text => :tag_list ).deserialize #This is what I'm currently using and is wrong
Response codes from log:
<- (GET 49996946161 2204098100) http://www.viralheat.com:80/api/sentiment/review.json?api_key=MY_KEY&text=tag_list
-> (GET 49996946161 2204098100) 200 OK (62 bytes 3.09s)
Looking up the docs for viralheat, it looks like their api accepts exactly two parameters: api_key, and text. Assuming params[:brand][:tag_list] a comma-delimited string, you can form your request like so:
current_user.tag(#brand, :with => params[:brand][:tag_list], :on => :tags)
url = "http://www.viralheat.com/api/sentiment/review.json"
#sentiment_response = url.to_uri.get(
:api_key => 'MY_KEY',
:text => params[:brand][:tag_list].split(',').join('&') ).deserialize
This should create the url:
http://www.viralheat.com/api/sentiment/review.json?api_key=MY_KEY&text=cat%26dog%26​mouse
params[:brand][:tag_list].split(',') breaks your string into an array, and join('&') turns it back into a string, but this time delimited by ampersands (which seems to be what you want, based on what you said in a comment on your original post). Your uri.get method should escape the ampersands in the uri, which is why you see the %26s in the final url. This is correct.

Resources