Seeding Data from JSON API to Ruby on Rails APP - ruby-on-rails

I'm getting a no implicit conversion of String into Integer error that has me stumped, and unable to import user records and seed my database with them.
So far I have no problem accessing the data, but receive an error referencing the '[]' on the line with User.find... on it
The code I'm using is as follows:
require 'net/http'
require 'uri'
require 'json'
require 'faker'
#this script imports APR user data from the zendesk api and populates
the database with it.
uri = URI.parse("https://blahsupport.zendesk.com/api/v2/users.json")
request = Net::HTTP::Get.new(uri)
request.content_type = "application/json"
request.basic_auth("blah#blah.com", "blahpass")
req_options = {
use_ssl: uri.scheme == "https",
}
#response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts #response.body
puts #response.message
puts #response.code
info = #response.body
info.force_encoding("utf-8")
File.write('blahusers1.json', info)
puts "File Created Successfully!"
file = File.read('blahusers1.json')
users = JSON.load(file)
users.each do |a|
User.find_or_create_by_zendesk_id(:zendesk_id => a['id'], :url => a['url'], :name => a['name'], :email => a['email'])
end
Any ideas on how I've gotten this error? Thank you for any help!
**Edit
Below is an example of the data being returned.
{"users":[{"id":333653859,"url":"https://blahblah.zendesk.com/api/v2/users/333653859.json","name":"Randy Blah","email":"randy#blah.com","created_at":"2014-08-06T14:31:24Z","updated_at":"2018-04-04T14:22:06Z","time_zone":"Pacific Time (US & Canada)","phone":null,"shared_phone_number":null,"photo":{"url":"https://aprtechsupport.zendesk.com/api/v2/attachments/68955389.json","id":68955389,"file_name":"Work.jpg","content_url":"https://aprtechsupport.zendesk.com/system/photos/6895/5389/Work.jpg","mapped_content_url":"https://blahblah.zendesk.com/system/photos/6895/5389/Work.jpg","content_type":"image/jpeg","size":2528,"width":80,"height":80,"inline":false,"thumbnails":[{"url":"https://blahblah.zendesk.com/api/v2/attachments/68955399.json","id":68955399,"file_name":"Work_thumb.jpg","content_url":"https://blahblah.zendesk.com/system/photos/6895/5389/Work_thumb.jpg","mapped_content_url":"https://blahblah.zendesk.com/system/photos/6895/5389/Work_thumb.jpg","content_type":"image/jpeg","size":2522,"width":32,"height":32,"inline":false}]},"locale_id":1,"locale":"en-US","organization_id":null,"role":"admin","verified":true,"external_id":null,"tags":[],"alias":"","active":true,"shared":false,"shared_agent":false,"last_login_at":"2018-04-04T14:21:44Z","two_factor_auth_enabled":null,"signature":"Thanks for contacting the helpdesk!\n-Randy","details":"","notes":"","role_type":null,"custom_role_id":null,"moderator":true,"ticket_restriction":null,"only_private_comments":false,"restricted_agent":false,"suspended":false,"chat_only":false,"default_group_id":21692179,"user_fields":{}}

The example data you posted has a root object users that contains the array of user objects. So when you loop users using users.each, a is actually an Array and not a user Hash like you expected.
When you try to access an element of an Array using a 'String' index, it gives you the exception – no implicit conversion of String into Integer
So, try changing
users = JSON.load(file)
to
users = JSON.load(file)['users']
to get it working like how you'd expect.

Related

Consuming paginated resources using HTTP with Ruby on Rails

I'm building out a platform for displaying data in charts that pulls from Zendesk's API. I'm running into trouble in that only 100 records at a time can be pulled with one call. How do I pull multiple pages of records from this resource?
Here is the code I use to make the call:
require 'net/http'
require 'uri'
require 'json'
#imports User data from the zendesk api and populates the database with it.
uri = URI.parse("https://samplesupport.zendesk.com/api/v2/users.json")
request = Net::HTTP::Get.new(uri)
request.content_type = "application/json"
request.basic_auth("sampleguy#sample.com", "samplepass")
req_options = {
use_ssl: uri.scheme == "https",
}
#response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts #response.body
puts #response.message
puts #response.code
This works fine for calling down one 'page' of resources...any help with grabbing multiple pages using my script would be greatly appreciated. Thank you!
Based on ZenDesk's documentation they return a next_page attribute in their payload. So you should just check for its existence and then query again if it exists. Repeat as needed.
require 'json'
# setup to query for the first page
results = JSON.parse(#response.body)
users = results['users'] #to get the users
if results['next_page']
# Do another query to results['next_page'] URL and add to users list

Use Hash value on url with Nokogiri or RestClient

I have a url like :
http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das
In my browser I can get data from that url, but if I'm use nokogiri
Nokogiri::HTML(open('http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das'))
I get
URI::InvalidURIError: bad URI(is not URI?): http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das
from /home/worka/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/uri/common.rb:176:in `split'
Also with RestClient
RestClient.get 'http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das'
I got same an error.
Encode your url first then use it.
url = 'http://172.0.0:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das'
encoded_url = CGI::escape(url)
Nokogiri::HTML(open(encoded_url))
When dealing with URIs, it's a good idea to use the tools designed for them such as URI, which comes with Ruby.
The URI can't be
http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das
because the data component is invalid. If you are adding data then I'd start with:
require 'uri'
uri = URI.parse('http://172.0.0.1:22230/test.action?sign=x6das')
query = URI.decode_www_form(uri.query).to_h # => {"sign"=>"x6das"}
data = {"foo" => "bar","joe" => "doe"}
uri.query = URI.encode_www_form(query.merge(data)) # => "sign=x6das&foo=bar&joe=doe"
uri.to_s # => "http://172.0.0.1:22230/test.action?sign=x6das&foo=bar&joe=doe"
Your initial example using {"foo":"bar","joe":"doe"} is JSON serialized data, which usually isn't passed in a URL like that. If you need to create JSON, start with the initial hash:
require 'json'
data = {"foo" => "bar","joe" => "doe"}
data.to_json # => "{\"foo\":\"bar\",\"joe\":\"doe\"}"
to_json serializes the hash into a string, which could then be encoded into the URI:
data = {"foo" => "bar","joe" => "doe"}
uri = URI.parse('http://172.0.0.1:22230/test.action?sign=x6das')
query = URI.decode_www_form(uri.query).to_h # => {"sign"=>"x6das"}
uri.query = URI.encode_www_form(query.merge('data' => data.to_json)) # => "sign=x6das&data=%7B%22foo%22%3A%22bar%22%2C%22joe%22%3A%22doe%22%7D"
But again, sending encoded JSON as a query parameter in the URI is not very common or standard since data payload is smaller without the JSON encoding.
Ok I got solved my problem
url = http://172.0.0.1:22230/test.action?data={"foo":"bar","joe":"doe"}&sign=x6das
RestClient.get(URI.encode(url.strip))

Rails Facebook avatar to data-uri

I'm trying to pull a facebook avatar via auth. Here's what i'm doing:
def image_uri
require 'net/http'
image = URI.parse(params[:image]) # https://graph.facebook.com/565515262/picture
fetch = Net::HTTP.get_response(image)
based = 'data:image/jpg;base64,' << Base64.encode64(fetch)
render :text => based
end
I'm getting the following error (new error — edited):
Connection reset by peer
I've tried googling about, I can't seem to get a solution, any ideas?
I'm basically looking for the exact functioning of PHP's file_get_contents()
Try escaping the URI before parsing:
URI.parse URI.escape(params[:image])
Make sure that params[:image] does contain the uri you want to parse... I would instead pass the userid and interpolate it into the uri.
URI.parse URI.escape("https://graph.facebook.com/#{params[:image]}/picture)"
Does it throw the same error when you use a static string "https://graph.facebook.com/565515262/picture"
What does it say when you do
render :text => params[:image]
If both of the above don't answer your question then please try specifying the use of HTTPS-
uri = URI('https://secure.example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https').start do |http|
request = Net::HTTP::Get.new uri.request_uri
response = http.request request # Net::HTTPResponse object
end
Presuming you are on ruby < 1.9.3, you will also have to
require 'net/https'
If you are on ruby 1.9.3 you don't have to do anything.
Edit
If you are on the latest version, you can simply do:
open(params[:image]) # http://graph.facebook.com/#{#user.facebook_id}/picture

Can't send POST request and get response correctly using NET::HTTP or httpclient?

I need to send different request with headers and body to PayPal. I want to use standatd class NET::HTTP, so here is my code(ISN'T WORKING):
require "net/http"
require "uri"
header = {...}
body = {...}
url = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
uri = URI.parse(url)
args = { 'header' => header,'body' => body }
res = Net::HTTP.post_form(uri, args)
puts res.status
Gives me error:
C:/Ruby193/lib/ruby/1.9.1/net/protocol.rb:141:in `read_nonblock': An existing connection was forcibly closed by the remote host. (Errno::ECONNRESET)
EDIT
Second variant:
require 'httpclient'
require 'xmlsimple'
header = {"X-PAYPAL-SECURITY-USERID" => "tok261_biz_api.abc.com",
"X-PAYPAL-SECURITY-PASSWORD" => "1244612379",
"X-PAYPAL-SECURITY-SIGNATURE" => "lkfg9groingghb4uw5",
"X-PAYPAL-REQUEST-DATA-FORMAT" => "NV",
"X-PAYPAL-RESPONSE-DATA-FORMAT" => "XML",
"X-PAYPAL-APPLICATION-ID" => "APP-80W284485P519543T"
}
#data to be sent in the request
data = {"emailAddress" => "denmed_1342605975_biz#gmail.com",
"firstName"=> "Den",
"lastName" => "Med",
"matchCriteria"=> "NAME",
"requestEnvelope.errorLanguage" => "en_US"}
#initialize the request
clnt = HTTPClient.new
#API end point(sandbox)
uri = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
#make the post
res = clnt.post(uri, data, header)
if res.status == 200
#xml = XmlSimple.xml_in(res.content)
if #xml['accountType']!=nil
account_type = #xml['accountType'][0]
#its pretty obvious from here init?
if account_type.to_s() == "Business"
puts "Business account!"
elseif account_type.to_s() == "Premier"
puts "Premier Account!"
end
elseif account_type.to_s() == "Personal"
puts "Personal account!"
else
puts "Account type not null but not a valid PayPal account type."
end
else
puts "Gee! sorry! something went seriously wrong"
end
This method - constantly gives me - Account type not null but not a valid PayPal account type.
But it is verified in Sandbox ! Tried to leave blank field, but it gave me the same !
Thanks for help in advance !
I am not familiar with your second code example, however I have used the first before (Net::HTTP) and I find it works quite well. I have generally only used it for GET, but I will try to advise on POST :)
First Thing: Headers should not be set as data to be sent with the request (which it appears you are doing) - instead, each header should be set as an attribute of the request:
request['X-HEADER_NAME'] = header_value
Here is how I suggest your code block should look if using Net::HTTP:
require "net/http"
require "uri"
url = "https://svcs.sandbox.paypal.com/AdaptiveAccounts/GetVerifiedStatus"
uri = URI.parse(url)
req = Net::HTTP::Post.new(url)
req["X-PAYPAL-SECURITY-USERID"] = "tok261_biz_api.abc.com"
req["X-PAYPAL-SECURITY-PASSWORD"] = "1244612379"
req["...."] = "...." .... etc for all headers
req.set_form_data( {"emailAddress" => "denmed_1342605975_biz#gmail.com",
"firstName"=> "Den",
"lastName" => "Med",
"matchCriteria"=> "NAME",
"requestEnvelope.errorLanguage" => "en_US"} )
Net::HTTP.start(uri.host, uri.port) do |http|
response = http.request(req)
return response
end
I suggest giving something like that a try, and if it doesn't work, can you give me the exact wording of the given error?
Note
Another suggestion is that, whenever my application receives Errno::ECONNRESET, it means that the back end server is not reachable (in this case I guess that would be the paypal server) - Are you positive the server is running? And are you positive there are no firewalls or anything in place which are preventing you from connecting?

invalid URI - How to prevent, URI::InvalidURIError errors?

I got the following back from delayed_job:
[Worker(XXXXXX pid:3720)] Class#XXXXXXX failed with URI::InvalidURIError: bad URI(is not URI?): https://s3.amazonaws.com/cline-local-dev/2/attachments/542/original/mac-os-x[1].jpeg?AWSAccessKeyId=xxxxxxxx&Expires=1295403309&Signature=xxxxxxx%3D - 3 failed attempts
The way this URI comes from in my app is.
In my user_mailer I do:
#comment.attachments.each do |a|
attachments[a.attachment_file_name] = open(a.authenticated_url()) {|f| f.read }
end
Then in my attachments model:
def authenticated_url(style = nil, expires_in = 90.minutes)
AWS::S3::S3Object.url_for(attachment.path(style || attachment.default_style), attachment.bucket_name, :expires_in => expires_in, :use_ssl => attachment.s3_protocol == 'https')
end
That being said, is there some type of URI.encode or parsing I can do to prevent a valid URI (as I checked the URL works in my browser) for erroring and killing delayed_job in rails 3?
Thank you!
Ruby has (at least) two modules for dealing with URIs.
URI is part of the standard library.
Addressable::URI, is a separate gem, and more comprehensive, and claims to conform to the spec.
Parse a URL with either one, modify any parameters using the gem's methods, then convert it using to_s before passing it on, and you should be good to go.
I tried ' open( URI.parse(URI.encode( a.authenticated_url() )) ' but that errord with OpenURI::HTTPError: 403 Forbidden
If you navigated to that page via a browser and it succeeded, then later failed going to it directly via code, it's likely there is a cookie or session state that is missing. You might need to use something like Mechanize, which will maintain that state while allowing you to navigate through a site.
EDIT:
require 'addressable/uri'
url = 'http://www.example.com'
uri = Addressable::URI.parse(url)
uri.query_values = {
:foo => :bar,
:q => '"one two"'
}
uri.to_s # => "http://www.example.com?foo=bar&q=%22one%20two%22"

Resources