Proper forming of API requests - ruby-on-rails

The following command in the console:
curl 'https://3b803ce956aa.ngrok.io/api/v1/send' -i --header "Accept: application/json" --header "Content-Type: application/json" -H 'Accept-Charset: utf-8' -H 'Authorization: Token token="GeUPm5xoxQFXR0ijJOZ6W6xr2ME1wZUWiaB3PLg9uZ8uGhFFDE7YnqCjFQwcCs0zgbtHjIiuc2jxo4I5"' -d '{"server_id":1,"building_id":1}'
but also with a version
--data '{"server_id":1,"building_id":1}'
is being received by the API controller. The routes are defined as:
namespace :api do
namespace :v1 do
post 'send', to: 'messages#send', defaults: { format: :json }
However, the handling of the requests is failing, in both cases, as it receives and returns:
Processing by Api::V1::MessagesController#send as JSON
Parameters: {"server_id"=>1, "building_id"=>1, "message"=>{"server_id"=>1, "building_id"=>1}}
Completed 500 Internal Server Error in 0ms (ActiveRecord: 0.0ms)
ArgumentError (wrong number of arguments (given 1, expected 0)):
where the controller action
def send
#request = JSON.parse(request.body.read)
#messagelog = Messagelog.create(server_id: params[:server_id], building_id: params[:building_id]
[...]
Two oddities appear:
the fact that the parameters are repeated with the contents of the parameters as part of the message
the action expects 0 arguments
I do not understand why the parameters get interpreted twice (this is a sub-question..). How can the API controller action be construed to accept the parameters?

By default Rails wraps JSON request parameters into a hash guessing its name from controller class. You can read details here.
So if you don't need this "guessed" hash in your params just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.

Related

Rails 5.2.3 converting all params datatype to string when testing using rspec

I am using rails 5.2.3 and testing using rspec-rails (3.8.2), when I send request to rails like this
let(:params) do
{
down_payment: 10_000,
asking_price: 100_000,
payment_schedule: 'weekly',
amortization_period: 5
}
end
it 'works' do
get :calculate, params: params, format: :json
expect(response.status).to eq 200
end
I also tried
it 'works' do
get :calculate, params: params, as: :json
expect(response.status).to eq 200
end
in rails all integers get converted to string like this
<ActionController::Parameters {"amortization_period"=>"5", "asking_price"=>"100000", "down_payment"=>"10000", "payment_schedule"=>"weekly", "format"=>"json", "controller"=>"payment_amount", "action"=>"calculate", "payment_amount"=>{}} permitted: false>
But if I use curl to send a request I can see integer not being converted to string.
curl -X GET -H "Content-Type: application/json" -d ‘{"asking_price": 100000 ,"payment_schedule": "monthly", "down_payment": 10000, "amortization_period": 5 }' http://localhost:3000/payment-amount
Thanks for any help!
JSON payloads can contain five value types: string, number, integer, boolean and null.
HTTP query strings are, by contrast, only strings.
By default, request specs use the encoding specified in the HTTP spec - i.e. all parameters are strings. This is why you see the parameters get converted.
If your production system is sending JSON, you need to tell the test to do so too - e.g. by adding as: :json as you did above.
Just add as: :json format to your requests
post(graphql_path, params: params, as: :json)

No route matches?

I'm getting a "No route matches" exception in one of my tests (and using curl from the command line) one of my routes (POST /users/confirm). The curl's I've tried are as follows, neither of them work and receive the same exceptions outlined in the below notes:
curl -X POST -H "Content-Type: application/json; version=1" \
-d '{ user: { "token":"1deb36b4e6a7ba6d9203" } }' \
http://localhost:3000/appname/users/confirm
curl -X POST -H "Content-Type: application/json; version=1" \
-d '{ "token":"1deb36b4e6a7ba6d9203" }' \
http://localhost:3000/appname/users/confirm
My test is as follows. I have config.wrap_parameters = true in my /config/application.rb file...
Here is my User UsersController#confirm action along with my strong params. I params.require(:user).permit(:token) as opposed to simply params.permit(:token) because, as stated above, I have config.wrap_parameters = true in my /config/application.rb file...
This is my route entry...
Here is the output from rails routes (app name removed)...
I have config.wrap_parameters = true in my /config/application.rb file...
Oddly enough, if I change my params in my test to post :confirm, params: { token: #user.confirmation_token }, I get the following error instead:
At a loss. Any thoughts?
It turns out I didn't need :token in my route after all. Changed it to this, and all is well:
post '/appname/users/confirm/', to: 'users#confirm', as: 'appname_users_confirm'

Error occurred while parsing request parameters JSON::ParserError - 795: unexpected token at

I am trying to write the API methods for user to sign up on spree app. It's working properly on my local machine but not on server. here is my code of user_decorator_controller.rb
def sign_up
#user = Spree::User.find_by_email(params[:user][:email])
if #user.present?
render "spree/api/users/user_exists", :status => 401 and return
end
#user = Spree::User.new(user_params)
if !#user.save
unauthorized
return
end
#user.generate_spree_api_key!
end
and sign_up.v1.rabl is
object #user
attributes :id, :spree_api_key, :email, :firstname, :lastname, :mobile
child(:bill_address => :bill_address) do
extends "spree/api/addresses/show"
end
child(:ship_address => :ship_address) do
extends "spree/api/addresses/show"
end
when I CURL the server with below request
curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST -d {"user":{"email":"ml5698#gmail.com","password":"12345678", "firstname":"M", "lastname":"L", "mobile":"9999888877"}} http://localhost:3000/api/users/sign_up
It gives me above error below is extracts from web server log
Started POST "/api/users/sign_up" for 127.0.0.1 at 2015-10-15 11:23:36 +0530
ActiveRecord::SchemaMigration Load (0.3ms) SELECT `schema_migrations`.* FROM `schema_migrations`
Error occurred while parsing request parameters.
Contents:
{user:{email:ml5698#gmail.com,password:12345678,
JSON::ParserError - 795: unexpected token at '{user:{email:ml5698#gmail.com,password:12345678,':
json (1.8.3) lib/json/common.rb:155:in `parse'
activesupport (4.2.4) lib/active_support/json/decoding.rb:26:in `decode'
I am using Ruby 2.2 , rails 4, json 1.8.3 , what could be the issue, please help me resolve it.
Your error is actually in your use of your command line curl. You use the -d switch, and pass parameters to it. The parameters are only parsed until the next space, so your parameters being passed in are
{user:{email:ml5698#gmail.com,password:12345678,
This is what you're seeing in the error message, and is obviously not well formed JSON, so you get the parsing error.
Try quoting your -d parameters like so:
curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST -d '{"user":{"email":"ml5698#gmail.com","password":"12345678","firstname":"M","lastname":"L","mobile":"9999888877"}}' http://localhost:3000/api/users/sign_up

How to test REST API request (rails)

I have REST API, for example: I already create REST users, for creating user end point is go to "localhost:3000/users" POST method.
So pattern of param should be like this :
:user => {:name => "apple", :address => "heaven"}
My question is :
How to create pattern above, then I test it to endpoint POST method ?
I already test with postman client, but failed because my pattern incorrect (miss root : user)
Thanks
A controller test will catch the type of error you're describing, and is more efficient than an end-to-end test. Example in RSpec:
it 'creates a new user' do
post :create, user: {name: 'apple', address: 'heaven'}, format: 'json'
assert_response :success
expect(response).to render_template(:create)
expect(assigns(:user).name).to eq('apple')
expect(assigns(:user).address).to eq('heaven')
end
I have used curl (in code) to test APIs end-to-end. The Faraday gem would probably work as well.
With the curl request, you'll need to pass the -X POST parameter and a -d parameter for the data. -d can have the following formats:
-d "user[name]=apple" -d "user[address]=heaven"
-d {"user": {"name": "apple", "address": "heaven"}}
You can also pass headers with -H.
Altogether:
curl http://your_url -X POST -H "Content-type: application/json" -d {"user": {"name": "apple", "address": "heaven"}}

Rails Status 406 POST response

I'm been struggling with this issue for the past 4 hours. Whenever I send in a POST request with no arguments (body="{}"), I get a response back with a 406 HTTP Status Error Code.
My controller looks as simple as
def resetFixture
respond_to do |format|
format.json { render :json=>final_obj, :status=>:ok}
end
end
Routes:
match 'TESTAPI/resetFixture' => 'users#resetFixture', :via => :post
I'm trying to return a JSON response with "{errCode:1}"
If I do a curl request with -d "" , I get the response I want, but how do I fix this error code? I appeared to have already specified JSON and the status.
It might be more intuitive if I also included the debug logs from the server for the failing request:
Since you can't request resetFixture.json then you'll need to add the following header to your request:
Accept: application/json
This will tell rails that you want JSON back.
You can test this with curl by adding the following argument: -H "Accept: application/json"
Alternatively, if you want Rails to always default to JSON you can change your route to:
match 'TESTAPI/resetFixture' => 'users#resetFixture', :via => :post, :defaults => { :format => 'json' }

Resources