Rails Baffling Result Parsing Array Param Sent Via Curl Multipart POST - ruby-on-rails

Please explain this, or provide a more-correct solution.
class Store < ApplicationRecord
has_and_belongs_to_many :books
end
class Book < ApplicationRecord
has_and_belongs_to_many :stores
end
I have several books in the database. I'd like to create a new store with several books in it.
The following few curl commands result in Rails parsing an empty array for params[book_ids]:
curl -F "author[book_ids[]]=[1,2]" -F "author[description]=some description" localhost:3000/authors.multipart
Started POST "/authors.multipart" for 127.0.0.1 at ...
Processing by AuthorsController#create as MULTIPART
Parameters: {"author"=>{"gesture_ids"=>[], "description"=>"some description", #headers ...
curl -F "author[book_ids[]]=\"[1,2]\"" -F "author[description]=some description" localhost:3000/authors.multipart
Started POST "/authors.multipart" for 127.0.0.1 at ...
Processing by AuthorsController#create as MULTIPART
Parameters: {"author"=>{"gesture_ids"=>[], "description"=>"some description", #headers ...
curl -F "author[book_ids[]]=1" -F "author[book_ids[]]=2" -F "author[description]=some description" localhost:3000/authors.multipart
Started POST "/authors.multipart" for 127.0.0.1 at ...
Processing by AuthorsController#create as MULTIPART
Parameters: {"author"=>{"gesture_ids"=>[], "description"=>"some description", #headers ...
But the following command does the trick:
curl -F "author[book_ids[]=1" -F "author[book_ids[]=2" -F "author[description]=some description" localhost:3000/authors.multipart
Started POST "/authors.multipart" for 127.0.0.1 at ...
Processing by AuthorsController#create as MULTIPART
Parameters: {"author"=>{"gesture_ids"=>["1","2"], "description"=>"some description", #headers ...
As you can see, the command is a missing a right brace in a couple of places, but it works. Checking the trace, curl is indeed sending the following for the first book.
0000: HTTP/1.1 100 Continue
=> Send data, 502 bytes (0x1f6)
0000: --------------------------538dd4bc2333141c
002c: Content-Disposition: form-data; name="store[book_ids[]"
0068:
006a: 1
006f: --------------------------538dd4bc2333141c

The correct syntax of author[book_ids][]=1
The syntax in this case is wrapper[key] = value (like you have for description) so for an Array we state that it is an Array of values wrapper[key][] then the = appends the value to the Array.
Write now author[book_ids[]]=[1,2] would technically be parsed as author: {book_ids: []} = [1,2] which is clearly incorrect and the parser just ignores the assignment.

Related

Proper forming of API requests

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.

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)

Can't figure out how to update users with devise_token_auth gem

I'm working on a rails API that is meant to work with a VUE frontend. This is my first time working with rails as an API only.
I'm familiar with using devise for user authentication in other rails apps.
Our sign in works, same with logout. I can't figure out how to edit and delete users though.
My rake routes shows this:
new_api_user_session GET /api/v1/auth/sign_in(.:format) devise_token_auth/sessions#new
api_user_session POST /api/v1/auth/sign_in(.:format) devise_token_auth/sessions#create
destroy_api_user_session DELETE /api/v1/auth/sign_out(.:format) devise_token_auth/sessions#destroy
new_api_user_password GET /api/v1/auth/password/new(.:format) devise_token_auth/passwords#new
edit_api_user_password GET /api/v1/auth/password/edit(.:format) devise_token_auth/passwords#edit
api_user_password PATCH /api/v1/auth/password(.:format) devise_token_auth/passwords#update
cancel_api_user_registration GET /api/v1/auth/cancel(.:format) devise_token_auth/registrations#cancel
new_api_user_registration GET /api/v1/auth/sign_up(.:format) devise_token_auth/registrations#new
edit_api_user_registration GET /api/v1/auth/edit(.:format) devise_token_auth/registrations#edit
api_user_registration PATCH /api/v1/auth(.:format) devise_token_auth/registrations#update
My routes.rb is like so:
Rails.application.routes.draw do
root 'home#index'
namespace :api do
scope :v1 do
resource :profile, only: [:show]
mount_devise_token_auth_for "User", at: 'auth'
## other code
end
end
end
I'm testing the api using CURL. The following command can successfully log in:
curl -X POST -v -H 'Content-Type: application/json' http://localhost:3000/api/v1/auth/sign_in -d '{"email": "test#example.com", "password": "password"}'
That works OK.
The route for updating a user should be http://localhost:3000/api/v1/auth/
The sign in gives me the client and access-token values. When I pop those into the following curl command I expect the user to update. The attribute that I'm trying to update is "sortOrder".
curl -X PATCH -v -H 'Content-Type: application/json' -H 'access-token: XUbhcAgWlVnARf9Ps4rjR' -H 'client: Lxg5us7MpUyAuM5xQGNuqg' -H 'uid: test#example.com' http://localhost:3000/api/v1/auth/ -d '{"sortOrder": "DESC"}'
From that command, my rails log shows:
Started PATCH "/api/v1/auth/" for 127.0.0.1 at 2018-12-26 12:10:53 -0800
Processing by DeviseTokenAuth::RegistrationsController#update as */*
Parameters: {"sortOrder"=>"DESC", "registration"=>{"sortOrder"=>"DESC"}}
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`uid` = 'test#example.com' LIMIT 1
Unpermitted parameter: registration
Completed 404 Not Found in 101ms (Views: 0.3ms | ActiveRecord: 0.6ms)
The output from the curl command is:
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> PATCH /api/v1/auth/ HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Type: application/json
> access-token: XUbhcAgWlVnARf9Ps4rjR
> client: Lxg5us7MpUyAuM5xQGNuqg
> uid: test#example.com
> Content-Length: 50
>
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 404 Not Found
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-cache
< X-Request-Id: 1e5e61f6-1226-43dc-b8b3-30a576b0c03a
< X-Runtime: 0.104965
< Vary: Origin
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
{"success":false,"errors":["User not found."],"status":"error"}
I'm not sure what I'm missing here. With this devise set up I don't have access to the devise/registrations controller. I could probably create my own but this should work. I haven't been able to find documentation for the devise_token_auth gem that addresses user updates and delete.
The result that I'm getting seems to be "user not found". I've tried several ways to get the user UID, email and id in the request but nothing seems to work. Has anyone experienced this before?
Looks like the user does not get saved somehow. I would try adding :create to the resource :profile, only: [:show, :create] in routes.rb. I am just learning RoR an Devise so I can't say that it is a 100% cure but worth a try. Following the same logic, you can add the :edit and :destroy actions to edit/delete profile/user.

Curl JSON POST request to Rails app

I am newbie in Rails and I am trying to create an engine in rails. The point is to send JSON data to app in this format {city: "Berlin", country: "Germany"} and receive back same JSON, but with reversed values like {city: "nilreB"} and so on.
My route is:
Rails.application.routes.draw do
post "collect/data" => 'data#create'
end
My controller is:
class DataController < ApplicationController
def create
render text: "#{request.body}"
end
end
I know that controller is not finished at all, but I am wondering why I am getting HTTP error when I am making POST request like:
curl -v -d '{"key1":"value1", "key2":"value2"}' http://0.0.0.0:3000/collect/data --header "Accept: application/json" --header "Content-Type: application/json"
HTTP/1.1 204 No Content
I have read that is fine when you use Rails 5, I am using it. So my question is am I missing something in controller or somewhere else? I would be much appreciated for any help.
These are logs:
INFO -- : [df2105a8-e2c5-4e27-a9c7-ffc91c8b92e8] Started POST "/collect/data" for 127.0.0.1 at 2018-02-09 20:26:49 +0000
INFO -- : [df2105a8-e2c5-4e27-a9c7-ffc91c8b92e8] Processing by DataController#create as JSON
INFO -- : [df2105a8-e2c5-4e27-a9c7-ffc91c8b92e8] Parameters: {"key1"=>"value1", "key2"=>"value2", "datum"=>{"key1"=>"value1", "key2"=>"value2"}}
INFO -- : [df2105a8-e2c5-4e27-a9c7-ffc91c8b92e8] No template found for DataController#create, rendering head :no_content
INFO -- : [df2105a8-e2c5-4e27-a9c7-ffc91c8b92e8] Completed 204 No Content in 1ms
As #ma_il said my parameters are available in DataController as params[:datum], I have searched a bit more about params in Rails 5.1 which I am using and discovered that my full amswer is:
def create
my_hash = params[:datum].permit!
render json: my_hash.to_h.collect { |keys, values| "#{keys}{values.reverse}" }
end

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

Resources