Ruby example of Net::HTTP for GET, POST, PUT, DELETE - ruby

I'm trying to learn Ruby for the first time. I have some experience in PHP and in PHP, I made a function like
function call_api(endpoint,method,arr_parameters='')
{
// do a CURL call
}
Which I would use like
call_api('https://api.com/user','get','param=1&param=2');
call_api('https://api.com/user/1','get');
call_api('https://api.com/user/1','post','param=1&param=2');
call_api('https://api.com/user/1','put','param=1&param=2');
call_api('https://api.com/user/1','delete');
So far, I've only learned how to do a GET and POST call with Ruby like so:
conn = Net::HTTP.new(API_URL, API_PORT)
resppost = conn.post("/user", 'param=1', {})
respget = conn.get("/user?param=1",{})
But I don't know how to do a delete and put. Can someone show sample code for the delete and put calls with the Net::HTTP object?

You would just namespace it:
Net::HTTP::Put.new(uri)
Same with delete:
Net::HTTP::Delete.new(uri)
You can even do that with your existing calls:
conn = Net::HTTP.new(uri)
con.get(path)
that is equivalent to:
Net::HTTP::Get.new(uri)

For DELETE you can use conn.delete("/user/1", {}) or
request = Net::HTTP::Delete.new("/user/1")
response = conn.request(request)
For PUT,
response = http.set_request('PUT', "/user/1", "param=1")
or
Net::HTTP::Put.new(path)

I like the Faraday gem. I find its design the simplest.
Once you gem install faraday you can require 'faraday' and do:
result = Faraday.get('http://google.es')
You can also POST, PUT, DELETE, etc.
Faraday.delete('http://google.es')
Faraday.post('http://google.es', {some_parameter: 'hello'})
Project: https://github.com/lostisland/faraday

Can I suggest a look at httparty? They offer some really awesome examples right on their page to do exactly what you want to do.
response = HTTParty.get('https://api.stackexchange.com/2.2/questions?site=stackoverflow')
puts response.body, response.code, response.message, response.headers.inspect
And many more examples of calling different endpoints.

Related

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.

Stuck with HTTP GET authentication in Ruby

I am trying to write short script to do HTTP authentication using GET request
This makes GET request.
def try_login(u, p)
path1 = '/index.php'
path2 = '?uuser=#{myuser}&ppass=#{mypass}'
r = send_request_raw({
'URI' => "#{path1}#{path2}",
'method' => 'GET'
})
...continued...
But this code does not work because error says:
undefined local variable or method `myuser'
--> Basically I am trying to send one (1) GET request with login parameters, and the app responds with a specific data. And I do not know how to put placeholders for user and pass in this GET request.
...
Next, I am checking the HTTP response. Response comes in as JSON mime like this:
Success response
{"param1":1,"param2"="Auth Success","menu":0,"userdesc":"My User","user":"uuser","pass":"ppass","check":"success"}
Fail response
{"param1":-1,"param2"="Auth Fail","check":"fail"}
--> How can I check the response body for this kind of data.
I have been trying all day now, but stuck totally. Please advice.
Edit:
I do not understand why some one down voted this question saying little to no research on my part. Until before yesterday morning, I had absolutely zero idea about ruby code & working with it. And then I spent numerous hours looking at many different examples, making my script and testing it out. When it still didn't work, I asked my question here. Please, if you still want to down vote, do it but please, at least share some pointers to solve this as well.
def try_login(u, p)
path1 = '/index.php'
path2 = '?uuser=#{myuser}&ppass=#{mypass}'
r = send_request_raw({
'URI' => "#{path1}#{path2}",
'method' => 'GET'
})
...continued...
Should be:
def try_login(u, p)
path1 = '/index.php'
path2 = "?uuser=#{u}&ppass=#{p}"
r = send_request_raw({
'URI' => "#{path1}#{path2}",
'method' => 'GET'
})
...continued...
For parsing JSON in Ruby, I would recommend you take a look at this answer to another question.
Edit: The reason try_login(u, p) isn't working as you would expect is because Ruby does not do string interpolation for single quoted (') strings. Additionally, myuser and mypass do not appear to be the correct variables.

Ruby TestUnit, VCR and HTTP API Requests

I am building an API wrapper and am writing some tests for it and I have a couple of questions.
1) How do I write an assert for calls where data doesn't exist? For example, looking up a member by id using the API but the user won't exist yet.
2) How do I write an assert for testing PUT and DELETE requests?
I already have a grasp on testing GET and POST requests just not sure on the other 2 verbs.
For your question part 1...
You have a couple choices for data that doesn't exist:
You can create the data ahead of time, for example by using a test seed file, or a fixture, or a factory. I like this choice for larger projects with more sophisticated data arrangements. I also like this choice for getting things working first because it's more straightfoward to see the data.
You can create a test double, such as a stub method or fake object. I like this choice for fastest test performance and best isolation. For fastest tests, I intercept calls as early as possible. The tradeoff is that I'm not doing end-to-end testing.
For your question part 2...
You should edit your question to show your actual code; this will help people here answer you.
Is your VCR code is something like this?
VCR.use_cassette('test_unit_example') do
response = Net::HTTP.get_response('localhost', '/', 7777)
assert_equal "Hello", response.body
end
If so, you change the HTTP get to put, something like this:
uri = URI.parse(...whatever you want...)
json = "...whatever you want..."
req = Net::HTTP::Put.new(uri)
req["content-type"] = "application/json"
req.body = json
request(req)
Same for HTTP delete:
Net::HTTP::Delete.new(uri)
A good blog post is the http://www.rubyinside.com/nethttp-cheat-sheet-2940.html>Net::HTTP cheat sheet excerpted here:
# Basic REST.
# Most REST APIs will set semantic values in response.body and response.code.
require "net/http"
http = Net::HTTP.new("api.restsite.com")
request = Net::HTTP::Post.new("/users")
request.set_form_data({"users[login]" => "quentin"})
response = http.request(request)
# Use nokogiri, hpricot, etc to parse response.body.
request = Net::HTTP::Get.new("/users/1")
response = http.request(request)
# As with POST, the data is in response.body.
request = Net::HTTP::Put.new("/users/1")
request.set_form_data({"users[login]" => "changed"})
response = http.request(request)
request = Net::HTTP::Delete.new("/users/1")
response = http.request(request)

Post nested params (hash) using HTTPClient

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.

Resources