How to pass patch http request parameters via HTTParty gem? - ruby-on-rails

I am using the HTTParty gem to make a patch request to update an object via an API call as follows:
params = { first_name: "John"}
#options = {params: params}
#response = HTTParty.patch("http://localhost:3000/1/kites/4", #options)
But on the API side, within the update method that the above PATCH request is supposed to call I only see the following parameters available:
{"format"=>"json",
"controller"=>"api/v1/kites",
"action"=>"update",
"version"=>"1",
"id"=>"4"}
An error message is passed back to the #response for pasrsing.
What happened to first_name and/or how do I call HTTParty.patch appropriately if that is indeed what is causing the loss of the parameters passed to the API?
EDIT:
As it turns out, if I do
#options = {query: params}
that will work but only if I keep a query under a certain size....

Not sure what your 'patch' action does exactly in your API but the documentation says that you need to pass a URL and params in a body: key, like so:
HTTParty.patch('site/your_url', body: { key1: value, key2: value })
Alternalitvely, you can pass the params in query: key which appends the params to the URI.
HTTParty.patch('site/your_url', query: { key1: value, key2: value })

Related

How can I access the data for the snake object sent through JSON in my params?

Using javascript, I make a fetch post.
const game = {name: this.player, snake: this.snake, score: this.score, apple: this.apple, skull: this.skull, completed: this.completed}
return fetch("http://localhost:3000/games", {
method: "POST",
headers: {
"Content-Type": "application/json"},
body: JSON.stringify(game)
})
.then(resp => resp.json())
.then(json => Game.appendHighScores(json));
I access the data via params on the Ruby on Rails end. The data for params[:snake][:body] are supposed to look like "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, yet when I type them into the command line, they look like this:
[<ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>14} permitted: false>, <ActionController::Parameters {"x"=>16, "y"=>15} permitted: false>]
It is accessible via indexing, but I get everything along with the data I'm looking for.
I was hoping it would look like the original params when I typed it in
<ActionController::Parameters {"name"=>"Don", "snake"=>{"x"=>16, "y"=>15, "direction"=>"down", "speed"=>0, "directions"=>{"left"=>{"x"=>-1, "y"=>0}, "up"=>{"x"=>0, "y"=>-1}, "right"=>{"x"=>1, "y"=>0}, "down"=>{"x"=>0, "y"=>1}}, "image"=>{}, "body"=>[{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}]}, "score"=>0, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "completed"=>true, "controller"=>"games", "action"=>"create", "game"=>{"score"=>0, "skull"=>{"image"=>{}, "x"=>12, "y"=>12}, "apple"=>{"image"=>{}, "x"=>2, "y"=>10}, "completed"=>true}} permitted: false>
Anyway to get the params as an array without it looking so messy with ActionController::Parameters inside of the element?
The reason everything is wrapped inside ActionController::Parameters is for your security (mass assignment in particular). You should never trust data send from the internet. This class allows you to permit/whitelist what properties you trust and filter out everything that you don't trust.
snake_params = params.require(:snake).permit(body: [:x, :y])
You can then convert this into a hash with a simple to_h call, which will drill down into all other nested parameters that are also permitted.
snake_data = snake_params.to_h
#=> { "body" => [{"x"=>16, "y"=>15}, {"x"=>16, "y"=>14}, {"x"=>16, "y"=>15}] }
If you'd like to include other attributes as well you can add them to the permit list.
.permit(:direction, :speed, directions: {left: [:x, :y], up: [:x, :y], ...}, body: [:x, :y])
For more info about permit I suggest checking out the guide Action Controller Overview - 4.5 Strong Parameters.
If you don't care about permitting certain parameters you can permit everything with permit!.
Note that you don't have to permit parameters if you extract the values directly. The code below would work perfectly fine without permitting anything.
body = params[:snake][:body]
body.each |body_part|
x = body_part[:x]
y = body_part[:y]
// do stuff with x and y
end
Because Rails utilize "strong parameters" you have to list and allow the parameters in the controller you want to use.
Since you have a list of parameters with dynamic keys the easiest way to do, while you are in development mode is to permit them like this:
# in your controller
def game_params
params.require(:game).tap do |permitted|
permitted[:name] = params[:name].permit!
permitted[:snake] = params[:snake].permit!
permitted[:apple] = params[:apple].permit!
permitted[:skull] = params[:skull].permit!
permitted[:completed] = params[:completed].permit!
end
end
and then you will be able to access your params:
game_params[:snake][:body]
In production tho, I will encourage you to whitelist all of the keys, for example like this.

Simulate a POST request with the object parameters and an extra id

I'm doing RSpec tests in a ROR application and I need to simulate a post action in order to test a controller.
The normal use is like this:
post :candidate, candidate: FactoryGirl.attributes_for(:candidate)
My problem is that I also need to pass an id that comes from the url. The action url is this one:
candidates POST /positions/:id/candidate(.:format) positions#candidate
So what I need is something like this:
post :candidate, candidate: FactoryGirl.attributes_for(:candidate), id: position.id
How can I simulate a post request with object attributes and also an id?
In the normal use the output in the rails server is this:
Parameters: {"utf8"=>"✓", "candidate"=>{"first_name"=>"John", "last_name"=>"Doe", "email"=>"john#gmail.com", "phone_number"=>"+351911111111", "skype_id"=>"qwe", "linkedin_url"=>"qwe", "why_work_with_us"=>"qwe", "resume_url"=>"qwe", "portfolio_url"=>"qwe"}, "id"=>"2"}
You can send all the required params in a params hash. So, build a params hash and then pass that to the post call like this:
let(:params) { { candidate: FactoryGirl.attributes_for(:candidate), id: position.id } }
post :candidate, params

Parsing JSON in Controller w/ HTTParty

In my controller I have the following code...
response = HTTParty.get('https://graph.facebook.com/zuck')
logger.debug(response.body.id)
I am getting a NoMethodError / undefined method `id'
If I do...
logger.debug(response.body)
It outputs as it should...
{"id":"4","name":"Mark Zuckerberg","first_name":"Mark","last_name":"Zuckerberg","link":"http:\/\/www.facebook.com\/zuck","username":"zuck","gender":"male","locale":"en_US"}
One would think it's response.body.id, but obviously that's not working. Thanks in advance!
Try this:
body = JSON.parse(response.body)
id = body["id"]
For this kind of thing, I'd recommend either a) using Koala or b) create a class using httparty. You can then set format: json to auto parse the returned json. See here and here
You can force the response to be treated as JSON using HTTParty.get like so:
response = HTTParty.get("http://itunes.apple.com/search",
{query: {term: 'tame impala'}, format: :json})
response['results'][0]['trackName']
=> "Let It Happen"
You can use response['id'] in case that the response Content-Type is application/json or also response.parse_response to get a Hash generated from the JSON payload.
response = HTTParty.get('https://graph.facebook.com/zuck')
payload = response.parsed_response
logger.debug(payload['id'])
Note: parsed_response is a Hash, only if the response Content-Type is application/json, otherwise HTTParty will return it as a string. For enforcing a Hash, in case the response does not return application/json, you can pass the format as a parameter HTTParty.get(url, format: :json).
HTTParty should automatically parse the content based on the content type returned. Something fishy seems to be going on with zuck's json.
pry(main)> HTTParty.get('https://graph.facebook.com/zuck')
=> "{\"id\":\"4\",\"first_name\":\"Mark\",\"gender\":\"male\",\"last_name\":\"Zuckerberg\",\"link\":\"https:\\/\\/www.facebook.com\\/zuck\",\"locale\":\"en_US\",\"name\":\"Mark Zuckerberg\",\"username\":\"zuck\"}"
But this works OK:
pry(main)> HTTParty.get('http://echo.jsontest.com/foo/bar/baz/foo')
=> {"baz"=>"foo", "foo"=>"bar"}
Don't forget to require 'httparty' if you're trying that in the console yourself.

RoR Net::HTTP post error undefined method `bytesize'

I am currently banging my head against the wall repeatedly until I get passed this issue. I'm using ruby-1.9.3-p194 and Rails. I'm attempting to make a post request which I can do fine with Net::HTTP.post_form, but I can't use that here because I need to set a cookie in the header. http.post is erroring saying
"undefined method `bytesize' for #<Hash:0xb1b6c04>"
because I guess it's trying to perform some operation on the data being sent.
Does anyone have some kind of fix or work around?
Thanks
headers = {'Cookie' => 'mycookieinformationinhere'}
uri = URI.parse("http://asite.com/where/I/want/to/go")
http = Net::HTTP.new(uri.host, uri.port)
response = http.post(uri.path, {'test' => 'test'}, headers)
The bytesize method is on String, not Hash. That's your first clue. The second clue is the documentation for Net::HTTP#post:
post(path, data, initheader = nil, dest = nil)
Posts data (must be a String) to path. header must be a Hash like { ‘Accept’ => ‘/’, … }.
You're trying to pass a Hash, {'test' => 'test'}, to post where it expects to see a String. I think you want something more like this:
http.post(uri.path, 'test=test', headers)

rails params hash to String:String hash

I am doing an http get using the url http://localhost/add?add_key[0][key]=1234&add_key[0][id]=1.
I have a rails app which gives me a neat params hash {"add_key"=>{"0"=>{"key"=>"1234", "id"=>"1"}}. However when I try to post this to a different server using
new_uri = URI.parse("http://10.10.12.1/test")
res = Net::HTTP.post_form new_uri,params
The server handling the post is seeing this parameter in the request
{"add_key"=>"0key1234id1"}
Looks like post_form requires a String to String hash. So how do I convert the params hash to
{"add_key[0][key]" => "1234", add_key[0][id]" => "1"}
From the fine manual:
post_form(url, params)
Posts HTML form data to the specified URI object. The form data must be provided as a Hash mapping from String to String.
So you're right about what params needs to be.
You could grab the parsed params in your controller:
{"add_key"=>{"0"=>{"key"=>"1234", "id"=>"1"}}
and then recursively pack that back to the flattened format that post_form expects but that would be a lot of pointless busy work. An easy way to do this would be to grab the raw URL and parse it yourself with URI.parse and CGI.parse, something like this in your controller:
u = URI.parse(request.url)
p = CGI.parse(u.query)
That will leave you with {"add_key[0][key]" => "1234", "add_key[0][id]" => "1"} in p and then you can hand that p to Net::HTTP.post_form.

Resources