Post nested params (hash) using HTTPClient - ruby

I needed a to have a multipart http post from one app to another that included a file attachment and a nested params hash. I tried using HTTPClient which worked for the file attachment, however I could not get params to send in a nested format.
data_params = Hash.new
data_params[:params] = Hash.new
data_params[:params][:f] = Hash.new
data_params[:params][:d] = Hash.new
data_params[:params][:d][:name] = "Mich"
data_params[:params][:d][:city] = "Ostin"
data_params[:params][:f][:event] = "Sundance"
http_client = HTTPClient.new
body = data_params[:params]
response = http_client.post('http://localhost:3030/receiver/receive_test_data/', body)
with the receiver app seeing the params as {"d"=>"nameMichcityOstin","f"=>"eventSundance"} (with the hash collapsed into strings on the nested level)
I wonder if this is a limitation on http posts or am I simply doing something wrong. I have worked with JSON before, which I know supports a nested structure, but there I have no idea how to add file attachments. I appreciate any suggestions or alternative methods that would comply with 'best practices' on doing something like this.

If using Rails:
> {:a=>53,:b=>{:c=>7}}.to_query
=> "a=53&b[c]=7"
http://apidock.com/rails/ActiveSupport/CoreExtensions/Hash/to_query

I'm not sure which HTTPClient library you're using so I haven't been able to try this, but what if you use keys like this
data_params[:params]['d[name]'] = "Mich"
data_params[:params]['d[city]'] = "Ostin"
i.e. data_params[:params] is just a one level hash.
and then the receiving application will unpack this into the nested hash that you expect.

Related

Ruby URI: build URI with "param[]=value" for array params

I'm trying to query an API (lichess API). And I can't get the params to be correctly taken into account, because URI formats the URL not in the expected format for array params.
The expected format is this one: curl https://explorer.lichess.ovh/lichess?ratings[]=2200&ratings[]=2500. So the ratings params is an Array of values, and the url should be using ratings[]=value.
I'm currently generating my URI like this, as per the doc:
base = "https://explorer.lichess.ovh/master"
params = {
ratings: [1600, 1800]
}
uri = URI(base)
uri.query = URI.encode_www_form(params)
But this generate the URL like this: https://explorer.lichess.ovh/master?ratings=1600&ratings=1800... which is not the expected format, and is not understood by Lichess API.
Does Ruby provide any built-in way to generate the params in the expected format, or will I need to build the URL manually (which feels like revinventing the wheel)?
Rails/ActiveSupport has a to_query method which will do what you want.
2.1.5 :010 > params.to_query
=> "ratings%5B%5D=1600&ratings%5B%5D=1800"
Also, have a look at this comment which explains why URI.encode_www_form doesn't return square brackets.

Ruby: HTTP Put method

I am attempting to update the 'ip' parameter in a json object in an API.
I have the following case:
when "put"
uri = URI.parse("http://#{ip}:#{port}/api/v1/address_data/1.json")
jobj = Hash.new
jobj['ip'] = "1.1.1.1"
http = Net::HTTP.new(uri.hostname, uri.port)
response = http.send_request('PUT', '/api/v1/address_data/1.json', data = jobj.to_s)
end
This does not work, but this does:
curl -X PUT http://ip:port/api/v1/address_data/1.json -d "ip=1.1.1.1"
How do I more accurately translate the curl into a Put request in Ruby? I have tried several methods I've found through google searching, but none of them have had successful results.
A few things:
You're not sending JSON in the Ruby example, it's a string representation of a Ruby hash which isn't the same. You need the JSON module or similar.
In the Ruby code you're attempting to send a JSON object (which would look like {"ip":"1.1.1.1"} and in the curl example you're sending it in application/x-www-form-urlencoded format, so they're currently not equivalent.
Also I'd look at the type of data the server expects from your requests: both Ruby and curl send a request header of Content-Type: application/x-www-form-urlencoded by default, and you're expecting to send JSON. This is why the curl example works: the data format you're using and the header matches. Note the .json in the URL shouldn't really make any difference; the header takes precedence.
Your call to send_request has you picking out the data parameter as a Python-style keyword argument. Ruby doesn't do that: what you're actually doing there is assigning a local variable in-line with the call.
So try something like this:
require 'json' # put this at the top of the file
uri = URI.parse("http://#{ip}:#{port}/api/v1/address_data/1.json")
jobj = {"ip" => "1.1.1.1"}
http = Net::HTTP.new(uri.hostname, uri.port)
response = http.send_request('PUT', uri.path, JSON.dump(jobj),
{'Content-Type' => 'application/json'})
And just a friendly reminder, saying something "doesn't work" doesn't usually give enough information to people that might answer your question: try and remember to paste in error messages, stack traces, and things like that :)

Ruby Sinatra and JSON objects from toodledo API 2.0

I have a small problem with receiving JSON objects. I'm using Ruby 1.9.3 and my goal is to receive my tasks from an API via RestClient and print them more or less pretty onto the page.
I created a route /test:
get '/test' do
json_ip_url = "http://api.toodledo.com/2/tasks/get.php?key=198196ae24792467eec09ac2191*****;modafter=1234567890;fields=folder,star,priority"
ip_details = RestClient.get(json_ip_url)
test = JSON.pretty_generate(ip_details) # => throws exception
end
The JSON#pretty_generate line throws an error, "only generation of JSON objects or arrays allowed". What am I doing wrong here?
Update:
I'am now able to output via pretty_generate, but what do I have to do, to get the elements of it. Here is the JSON Data, it seems to me its an Array with Objects inside of it?
[{"num":"18","total":"18"},{"id":"11980343","title":"Add some items to your todo list","modified":1391670256,"completed":0,"folder":"0","star":"0"},{"id":"11980345","title":"Visit the Settings section and configure your account","modified":1391670256,"completed":0,"folder":"0","star":"0"},{"id":"11980347","title":"Watch our tutorial videos in the Help section","modified":1391670256,"completed":0,"folder":"0","star":"1"},{"id":"12607789","title":"test","modified":1392285802,"completed":0,"folder":"0","star":"0"},{"id":"12636039","title":"My Task","modified":1392308705,"completed":0,"folder":"0","star":"0"},{"id":"12636041","title":"Another","modified":1392308705,"completed":0,"folder":"0","star":"1"},{"id":"12636143","title":"My Task","modified":1392308789,"completed":0,"folder":"0","star":"0"},{"id":"12636145","title":"Another","modified":1392308789,"completed":0,"folder":"0","star":"1"},{"id":"12636449","title":"My Task","modified":1392308950,"completed":0,"folder":"0","star":"0"},{"id":"12636451","title":"Another","modified":1392308950,"completed":0,"folder":"0","star":"1"},{"id":"12636621","title":"My Task","modified":1392309061,"completed":0,"folder":"0","star":"0"},{"id":"12636623","title":"Another","modified":1392309061,"completed":0,"folder":"0","star":"1"},{"id":"12636665","title":"My Task","modified":1392309085,"completed":0,"folder":"0","star":"0"},{"id":"12636667","title":"Another","modified":1392309085,"completed":0,"folder":"0","star":"1"},{"id":"12636733","title":"My Task","modified":1392309137,"completed":0,"folder":"0","star":"0"},{"id":"12636735","title":"Another","modified":1392309137,"completed":0,"folder":"0","star":"1"},{"id":"12637135","title":"My Task","modified":1392309501,"completed":0,"folder":"0","star":"0"},{"id":"12637137","title":"Another","modified":1392309501,"completed":0,"folder":"0","star":"1"}]
The Code I used for pretty_generate:
get '/save' do
jdata = params[:data]
response = RestClient.get 'http://api.toodledo.com/2/tasks/get.php?key=da21e24e2a00ba9d45008974aed00***;modafter=1234567890;fields=folder,star,priority', {:accept => :json}
test = JSON.parse(response)
test.to_json
output = JSON.pretty_generate(test)
puts output
RestClient#get returns the raw response as a string (and not a hash or array) when called without a block, so ip_details isn't a structure that JSON#pretty_generate knows how to handle. You need to use JSON#parse to turn the response into a hash or array first.

Ruby simple Web Service client performing a call to Solr

I need some help in doing this: I have to build the following URL in order to perform a query aganst an Apache Solr instance:
http://localhost:8080/solr/select?q=*%3A*&fq=deal_discount%3A[20+TO+*]&fq=deal_price%3A[*+TO+100]&fq={!geofilt+pt%3D45.6574%2C9.9627+sfield%3Dlocation_latlng+d%3D600}
As you can see, the URL contains 3 times the parameter named "fq". What I'm just wondering is how to use the URI.parse() method if I need to pass three times the parameter "fq" within the Hash that is the second argument of the parse() method.
Here's a simple snippet:
path = 'http://localhost:8080/solr/select'
pars = { 'fq' => 'deal_price [* TO 100]', 'fq' => '{!geofilt pt=45.6574,9.9627 sfield=location_latlng d=600}' } # This is obviously wrong!
res = Net::HTTP::post_form( URI.parse(path), pars )
The solution would be passing the full URL as a String, but I cannot find a method that provide this kind of signature.
Could you please post a simple solution to my problem? Thanks in Advance.
Thaks for your help. Yes, you're right... A get method was what I need. Anyway I had to make a little change to your code because Net:HTTP.get() threw an exception "Unknown method hostname"
uri = URI(solrUrl)
req = Net::HTTP::Get.new(uri.request_uri)
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
This solved my problem. Thanks indeed.
Your URL suggest that you should use HTTP GET to query solr while your snippet uses POST so that is one thing to change. But I think your main problem is with the parameters, a Hash may only contain one entry for a key so you can't use a Hash in this case. One easy way is to construct the URL by hand.
params_array = ['deal_price [* TO 100]',
'{!geofilt pt=45.6574,9.9627 sfield=location_latlng d=600}']
base_url = "http://localhost:8080/solr/select"
query_string = "?fq=#{params_array.join('&fq=')}"
url = base_url + query_string
result = Net::HTTP.get url
A bit compact maybe - a more readable version may be (according to taste):
params_array = ['deal_price [* TO 100]',
'{!geofilt pt=45.6574,9.9627 sfield=location_latlng d=600}']
url = "http://localhost:8080/solr/select&"
params_array.each do |param|
url << "&fq=#{param}"
end
result = Net::HTTP.get url

How do I extract a value from a JSON response?

I am writing some tests in Ruby using RestClient. The test is working fine and the response is in JSON however when I parse the JSON and try to extract the values I am looking for I get an error saying IndexError: key not found
IMO, my code should work. The JSON is:
{"user":{"#xmlns":{"dvi":"http:\/\/xxxx","a":"http:\/\/xxxx","$":"http:\/\/xxxx"},"link":[{"#rel":"self","$":"http:\/\/xxxx"},{"#rel":"user","$":"http:\/\/xxxx"},{"#rel":"usage","$":"xxxx"},{"#rel":"repositories","$":"http:\/\/xxxx"},{"#rel":"shares","$":"http:\/\/xxxx"},{"#rel":"shareMemberships","$":"http:\/\/xxxx"}],"phone":{"$":"3518012343001"},"email":{"$":""},"firstName":{"$":"Jim"},"lastName":{"$":"Joe"},"uid":{"$":"91bc7a72bc724e5e9b53e688dd105ed4"},"accountName":{"$":"3518012343001"},"notificationMethod":{"$":"email sms"},"accountStatus":{"$":"Active"},"serviceLevel":{"$":"5"},"repositoryCount":{"$":"1"},"usage":{"allowed":{"$":"5368709120"},"total":{"$":"1024"}},"contactEmail":{"$":"jim#joe.com"}}}
and my code is:
result = jsonabove
jdoc = JSON.parse(result)
notificationMethod = jdoc.fetch("notificationMethod")
return notificationMethod
That's happening because the notificationMethod key isn't the first level key in your hash. After preparing theJSON#parse method, you have a hash with only one key called user. You should get the value by this key and then apply your notificationMethod key. It looks like this:
require 'json'
result = <<HERE
{"user":{"......"}}
HERE
jdoc = JSON.parse(result)
notificationMethod = jdoc.fetch("user").fetch("notificationMethod")
puts notificationMethod

Resources