How do I iterate over this JSON object? - ruby-on-rails

This is an object that is returned as a response in an HTTP POST request:
res.body
=> "{\"id\":\"a3adasfaf3\",\"url\":\"https://someurl/a3adasfaf3\",\"created\":\"2016-05-30T07:00:58Z\",\"modified\":\"2016-05-30T07:00:58Z\",\"files_hash\":\"cljhlk2j3l2kj34hlke18\",\"language\":\"ruby\",\"title\":\"Some weird hello world message\",\"public\":false,\"owner\":\"kljhlk2jh34lk2jh4l2kj3h4l2kj4h23l4kjh2l4k\",\"files\":[{\"name\":\"Some-weird-hello-world-message.rb\",\"content\":\"puts \\\"Some weird hello world message.\\\"\\r\\n\"}]}"
I am trying to pull out, and translate the various attributes of that response. For instance, at the very least the id and url.
How do I do this?
For the record, I am using Ruby's NET/HTTP std lib to send the POST request and get back this response.
Edit 1
For bonus points, all I want is the actual value stored in each attribute (i.e. the actual id (which is just a string) and a url (which is a typical URL). So if you included how I might both access that attribute and then sanitize it at the same time that would be awesome.

Use JSON.parse to parse the response.
response = "{\"id\":\"a3adasfaf3\",\"url\":\"https://someurl/a3adasfaf3\",\"created\":\"2016-05-30T07:00:58Z\",\"modified\":\"2016-05-30T07:00:58Z\",\"files_hash\":\"cljhlk2j3l2kj34hlke18\",\"language\":\"ruby\",\"title\":\"Some weird hello world message\",\"public\":false,\"owner\":\"kljhlk2jh34lk2jh4l2kj3h4l2kj4h23l4kjh2l4k\",\"files\":[{\"name\":\"Some-weird-hello-world-message.rb\",\"content\":\"puts \\\"Some weird hello world message.\\\"\\r\\n\"}]}"
require 'json'
JSON.parse response
# output:
# {"id"=>"a3adasfaf3", "url"=>"https://someurl/a3adasfaf3", "created"=>"2016-05-30T07:00:58Z", "modified"=>"2016-05-30T07:00:58Z", "files_hash"=>"cljhlk2j3l2kj34hlke18", "language"=>"ruby", "title"=>"Some weird hello world message", "public"=>false, "owner"=>"kljhlk2jh34lk2jh4l2kj3h4l2kj4h23l4kjh2l4k", "files"=>[{"name"=>"Some-weird-hello-world-message.rb", "content"=>"puts \"Some weird hello world message.\"\r\n"}]}
response["name"] # => a3adasfaf3

You need to parse it with JSON.parse
Example:
parsed_hash = JSON.parse res.body
Result:
{
"id" => "a3adasfaf3",
"url" => "https://someurl/a3adasfaf3",
"created" => "2016-05-30T07:00:58Z",
"modified" => "2016-05-30T07:00:58Z",
"files_hash" => "cljhlk2j3l2kj34hlke18",
"language" => "ruby",
"title" => "Some weird hello world message",
"public" => false,
"owner" => "kljhlk2jh34lk2jh4l2kj3h4l2kj4h23l4kjh2l4k",
"files" => [
[0] {
"name" => "Some-weird-hello-world-message.rb",
"content" => "puts \"Some weird hello world message.\"\r\n"
}
]
}
To access the id:
parsed_hash['id']
To access the url:
parsed_hash['url']
Want to access it by symbols ?
parsed_hash = JSON.parse(res.body).symbolize_keys
You can now access id and url by parsed_hash[:id] and parsed_hash[:url]

Related

Setting a blank custom header in ruby on rails

I'm writing a REST server using Ruby on rails.
Some of the communication with the client is by setting certain custom headers to predefined values. In certain situations the server is expected to respond with a custom header that is an empty string. My problem is that rails seems to omit any blank headers from the response (it will even omit a whitespace header).
This is my current code:
config.action_dispatch.default_headers = {
'X-WOPI-EmptyLock' => "",
'X-WOPI-FullLock' => "astring",
'Random-header' => "with value",
'Random-empty-header' => ""
}
But the response will only contain headers which are not empty strings:
Resulting headers in response
Curl response headers
Is there some way to configure rails to still send headers even if they are empty?
You can set your custom headers in config/application.rb file
Ex:
config.action_dispatch.default_headers = {
'Custom-Header1' => "", # without value
'Custom-Header2' => "with value"
}
Note: restart rails server after made changes
In response
Custome-Header1 →
Custome-Header2 →with value
Updated answer
Here is my controller code as below
def home
response.headers["My-Custome-Header"] = "With value"
response.headers["My-Custome-Header-2"] = "" # Without value
render json: {data: "It's working my friend"}
end
And required output as shown in screenshot
Hope this will help you.

Rails/Rspec JSON integers being converted to strings when testing post call

I am testing a JSON request to our API, It will respond with JSON.
It seems like all the integers within the JSON get converted to strings as we post them to the controller consider action.
Controller
def consider
binding.pry # binding no# 2 used to check the params after post from test.
if ParametersValidator.is_valid?(params)
application_handler = ApplicationHandler.new(request_interactor)
render json: application_handler.result
else
render json: ParametersValidator.failed_params(params).to_json
end
end
The ParamaterValidator validates the structure and types of data coming in.
Test
render_views
let(:json) { JSON.parse(response.body) }
..
..
it 'returns the result in the correct format for the AUTOMATIC APPROVE decision' do
automatic_approve_params = relative_json_file(relative_file('automatic_approve_params'))
expected_approve_params = {
"status" => "accepted",
"automated" => true,
"rate" => 6,
"amount" => 30000,
"term" => 10,
"pre_approved_amount" => 2500,
"comments" => ""
}
#request.headers['HTTP_X_AUTH_SIG'] = Rails.application.secrets['authorization']['token']
request.env["HTTP_ACCEPT"] = 'application/json'
binding.pry # binding no# 1 to inspect the params before post
post :consider, automatic_approve_params, format: :json
expect(json).to eq(expected_approve_params)
end
Binding no#1
{
"student_id"=>1,
"age"=>22,
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>100000000,
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
Binding no#2
{
"student_id"=>"1",
"age"=>"22",
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>"100000000",
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
The test log is showing that the request is
Processing by Api::V1::CreditApplicationsController#consider as JSON
Inspecting the request just before the post action you will see the params are fine, then in the controller before I run anything I inspect the params and they are all strings.
Using postman to test the API with the JSON works as expected but it seems that rspec when posting to the consider action will convert all the params to strings. I have read a few dozen posts that claim by adding format: :json to the post action it will remedy this, however I have had no such luck.
I am obviously doing something wrong but I have tried pretty much everything I know.
After replicating the issue you are having I managed to resolve it in a controller spec using the following:
post :consider, automatic_approve_params.merge(format: :json)
In my local tests I removed the
request.env["HTTP_ACCEPT"] = 'application/json' and it still worked as you expect it to. Hope it helps.
In Rails 5, use as: :json instead of format: :json, e.g. post :consider, params: automatic_approve_params, as: :json
We can try this
post 'orders.json', JSON.dump(order: {boolean: true, integer: 123}), "CONTENT_TYPE" => "application/json"

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 to post JSON data in rails 3 functional test

I plan to use JSON data in both request and response in my project and having some problems in testing.
After searching for a while, I find the following code which uses curl to post JSON data:
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d '{ "foo" : "bar" }' localhost:3000/api/new
In the controller I can access the JSON data simply using params[:foo] which is really easy. But for functional testing, I only find post and xhr (alias for xml_http_request).
How can I write functional test in rails to achieve the same effect as using curl? Or should I do test in other ways?
Here's what I've tried. I find the implementation for xhr in action_controller/test_case.rb, and tried to add jhr method simply changing 'Conetent-Type' and 'HTTP_ACCEPT'. (Added in test/test_helpers.rb.)
def json_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
#request.env['Content-Type'] = 'Application/json'
#request.env['HTTP_ACCEPT'] ||= [Mime::JSON, Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
__send__(request_method, action, parameters, session, flash).tap do
#request.env.delete 'Content-Type'
#request.env.delete 'HTTP_ACCEPT'
end
end
alias jhr :json_http_request
I used this in the same way as xhr, but it does not work. I inspected the #response object and sees the body is " ".
I also find one similar question on Stack Overflow but it's for rails 2 and the answer for posting raw data does not work in rails 3.
As of Rails 5, the way to do this is:
post new_widget_url, as: :json, params: { foo: "bar" }
This will also set the Content-type header correctly (to application/json).
I found that this does exactly what I want – post JSON to a controller's action.
post :create, {:format => 'json', :user => { :email => "test#test.com", :password => "foobar"}}
Just specify appropriate content type:
post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'
Json data should go as a string, not as a Hash.
Looking at stack trace running a test you can acquire more control on request preparation:
ActionDispatch::Integration::RequestHelpers.post => ActionDispatch::Integration::Session.process =>
Rack::Test::Session.env_for
Specifying :format does not work because request go as 'application/x-www-form-urlencoded' and json isn't parsed properly processing a request body.
Assuming you have a controller named api, a method named new, and you're in the test for the api controller:
#request.env["RAW_POST_DATA"] = '{ "foo" : "bar" }'
post :new
did the trick for me.
Here is a snippet that let me post json data to test my own app. rails 3
port = Rails.env.production? ? 80 : 3000
uri = URI.parse( Rails.application.routes.url_helpers.books_url(:host => request.host, :port => port, :format => :json) )
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = 'application/json'
request.body = #json_data
response = http.request( request )
#result = response.body
Hope this helps others
As #taro suggests in a comment above, the syntax that works for me in functional and integration tests is:
post :create, {param1: 'value1', param2: 'value2', format: 'json'}
(The curly braces aren't always necessary, but sometimes it doesn't work if they're missing, so I always add them.)
Here's what params and request.format look like for a post of that sort:
params:
{"param1"=>"value1", "param2"=>"value2", "format"=>"json", "controller"=>"things", "action"=>"create"}
request.format:
application/json
The best answer I can come up with to this is you don't
Whether or not it was intentional it s maybe good that rails doesn't implement this for you.
In functional tests you really want to just test your controller and not rails method of deserialization or even that routing and mime detection are all setup correctly, those all fall under an IntegrationTest.
So for your controllers, don't pass JSON just pass your params hash like you normally would. Maybe adding :format as an argument as well if you need to check that and respond differently.
If you want to test the full stack move to an IntegrationTest

Accessing data from httparty response

Using httparty I can get the following response:
puts Representative.find_by_zip(46544).inspect
-->
{"results"=>[{"name"=>"Joe Donnelly", "district"=>"2", "office"=>"1218 Longworth", "phone"=>"(202) 225-3915", "link"=>"http://donnelly.house.gov/", "state"=>"IN"}]
source of the example: http://railstips.org/blog/archives/2008/07/29/it-s-an-httparty-and-everyone-is-invited/
but I fail to access the data, for example:
Representative.find_by_zip(46544).inspect["name"] returns nil
How can I access individual elements of this response?
Object#inspect returns a string, not a hash. You want this:
Representative.find_by_zip(46544)['results'][0]['name']
This is what's going on: Representative#find_by_zip returns a Hash with just one index: 'results'. The item at 'results' is an array, which in this case only contains one element, so we use [0] to get the first (and only) element. That element is itself a hash that has the 'name' key, which points to the name of the first (and only) representative returned.
When you have complex hashes and arrays it's sometimes useful to format it in a more readable way to figure out how to get at the data you want:
{ "results" => [
{ "name" => "Joe Donnelly",
"district" => "2",
"office => "1218 Longworth",
"phone" => "(202) 225-3915",
"link" => "http://donnelly.house.gov/",
"state" => "IN"
}
]
}
That should make it more clear what's inside what here.
To Access the individual elements, You can use:-
If the response is JSON:-
You can use:-
response.parsed_response["key"]
If your response is a string then, you can use:-
data = JSON.parse(resp.body)
The response type depends, on the content type you are setting while requesting the data:-
'Content-Type' => 'application/json'
If you don't set the content type it returns response as string.

Resources