Change response required for Rails authentication - ruby-on-rails

I've a user model and a login route. Using ember-simple-auth, whenever I'm sending the data for authentication, the data is required to be in this format:
{"password"=>"[FILTERED]", "email"=>"admin#admin.com"}
However, I want the data to be in this format:
{"user"=>{"password"=>"[FILTERED]", "email"=>"admin#admin.com"}}
How can I change this?
Thanks.
Code: https://github.com/ghoshnirmalya/hub-server

As a possible approach you can override resoure_params method
class Api::V1::SessionsController < DeviseTokenAuth::SessionsController
private
def resource_params
params.permit(user: params_for_resource(:sign_in))[:user]
end
end
routes
namespace "api" do
namespace "v1" do
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
sessions: 'api/v1/sessions'
}
resources :users
end
end
Then if your params looks like
{
"user": {
"email": "nirmalya.email#gmail.com",
"password":"password"
}
}
you will receive success response:
{
"data": {
"id": 1,
"provider": "email",
"uid": "nirmalya.email#gmail.com",
"name": "Nirmalya Ghosh",
"nickname": null,
"image": null,
"email": "nirmalya.email#gmail.com"
}
}
But I'm strongly recommend to refactor your frontend to meet devise_token_auth reqirements and pass params as {email: "foo#mail.com", password: "password"}

Related

why i get a GET response instead a POST when i realize a api call

when I make a POST request in postman I see that the json is not sent to me and nothing is created but return a status: 200, if I look at the logs I see that this request is registered as GET, I don't understand this behavior, in other applications with the same configuration this does not happen,I don't know if the problem comes from the nginx / apache server or the rails server
here the plesk log:
this is my body test request
{
"event_id": "fd193bc1-992f-4e51-bb28-b841417f68a9",
"order_id": "61e2d090-0d53-451f-9031-413559d34732",
"order_number": "10103211120218",
"state": "fulfilled",
"store_id": "11",
"timestamp": "2021-06-03T12:06:02.576Z",
"delivery_details": {
"delivery_tracking_number": "003404342888680268172",
"delivery_carrier_name": "DHL",
"return_tracking_number": "35141263178",
"return_carrier_name": "DHL"
},
"customer_billing_address": {
"first_name": "Max",
"last_name": "Mustermann",
"address_line_1": "Musterstraße 12 34",
"zip_code": "12345",
"city": "Berlin",
"country_code": "DE"
},
"items": [
{
"item_id": "15c3aa83-3f73-4ad6-a326-e1a10a89dd52",
"ean": "4059701022541",
"price": 99.95,
"currency": "EUR",
"article_number": "31.832.34-6,5",
"zalando_article_number": "GA111N0JN-J110065000",
"article_location": "213"
},
{
"item_id": "101610aa-71b1-4f52-b3ab-4d6cbc4e8adb",
"ean": "4059701214472",
"price": 99.95,
"currency": "EUR",
"article_number": "33.731.54-7",
"zalando_article_number": "GA111N0JE-J110007000",
"article_location": "213"
}
]
}
the endpoint is https://www.somedomain.com/api/v1/orders/zalando
and the routes:
zalando_api_v1_orders POST /api/v1/orders/zalando(.:format) api/v1/orders#zalando {:format=>:json}
namespace :api, defaults: {format: :json} do
namespace :v1 do
resources :orders, only: [:index, :show, :update, :options] do
post 'zalando', on: :collection
end
end
end
and this is the class method with which I create the orders with what it should receive in the response
def call
if Order.where(order_code: [order_code_with_store,order_code]).any?
success
else
order = Order.create(order_params)
items = OrderItem.create(order_item_params(order.id))
if order.persisted? && items.all? { | item| item.persisted? }
UserMailer.marketplace_order_admin(order).deliver
success
else
success
end
end
rescue
order&.destroy
success
end

Why should POST data be nested?

I am using Rails as an API server, and I wonder why the data being sent to the server needs to be nested. This seems to be the preferred way of defining params:
def user_params
params.require(:user).permit(:first_name, :last_name, :password, :username, :email)
end
And this would be the corresponding JSON sent to the create route:
{
"user": {
"username": "lorem",
"first_name": "ipsum",
"last_name": "dolor",
"password": "sit",
"email": "amet"
}
}
Why is this the preferred way of posting data? Why could not the JSON be:
{
"username": "lorem",
"first_name": "ipsum",
"last_name": "dolor",
"password": "sit",
"email": "amet"
}
These aren't the only parameters that are sent while creating a resource for you, others are:
utf8 with value ✓
authenticity_token with a random string
commit with value either Save or Update
So the logic is pretty obvious: Rails groups all the user-belonging-parameters inside user key, and thus, it's easier to read, easier to interpret by the code, and easier to whitelist the related parameters.
Not only this, sometimes you will try to create multiple resources through one request, like a user has many books, so you would like to create a user, and at the same time, books - something called Nested Resources, and in that case, it will be like this:
{
"user":
{
"username": "john_don",
"books":
{
"0":
{
"author_id": 1
}
}
}
}
I hope you get the idea.

Devise Responding With 422 On Sign Up of User

Not sure why this is happening..I'm running against route /users.json and passing in the body as:
{
"email": "register#register.com",
"password": "ssssssssss"
}
I'm getting back: {"errors":{"email":["can't be blank"],"password":["can't be blank"]}}
uhmmm...what?
If you didn't change your controllers from the default scaffolds, the body should have "user" as the root key:
{
"user": {
"email": "register#register.com",
"password": "ssssssssss"
}
}
Also check your server logs that the parameters are actually accepted and parsed correctly.
Rolling back to version 3.3.2 of Devise fixed...and using #Ivan's req body of:
{
"user": {
"email": "register#register.com",
"password": "ssssssssss"
}
}

Rails errors - Define Position or Object in which the error occured

I have two models. For example Person and Address.
Because I want to add or update addresses to the person within one request, the person model looks like:
has_many :addresses
accepts_nested_attributes_for :addresses
In the address controller is only one validation
validates :city, presence: true
When I now update the user via json api it works like a charm:
{
"user": {
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb"},
{"street": "blu", "zip": "98765", "city": "blebb"}
]
}
}
Now I delete the city of the second record:
{
"user": {
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb"},
{"street": "blu", "zip": "98765"}
]
}
}
and in the Users controller I can render a json response, something like:
render json: #user.errors
which gives me the correct error.
I am missing, that I don't know which of the addresses threw the error (In this example the second).
Any Ideas?
You can return the entire user object with its nested attributes and errors. I.e.
render json: #user.as_json(
include: [{addresses: {methods: [:errors]}],
methods: [:errors]
)
The result should look like this:
{
"user": {
"errors": {...},
"addresses_attributes": [
{"street": "bla", "zip": "12345", "city": "blubb", "errors": {...}},
{"street": "blu", "zip": "98765", "errors": {...}}
]
}
}
The #user that you tried to create will still contain the addresses that it tried to create with the nested attributes.
I don't know exactly how you want to render the fact that an address failed the validation but you can identify the one(s) that did fail by iterating over the #user.addresses.
For example, this will return all the invalid addresses:
#user.addresses.select { |address| !address.valid? }
You can still render these objects, or the json representation of them, even though they haven't been saved to the database.

Rails permit nested array

I am trying to use accepts_nested_attributes_for in conjunction with a has_many association and having a lot of trouble...
Here is a simplified version of my user.rb:
class User < ActiveRecord::Base
...
has_many :user_permissions
accepts_nested_attributes_for :user_permissions
...
end
My user_permission.rb:
class UserPermission < ActiveRecord::Base
belongs_to :user
...
end
And my users_controller.rb:
class UsersController < ApiController
...
def update
#user.assign_attributes user_params
if #user.save
render partial: 'user', locals: { user: #user }
else
render json: {errors: #user.errors}.to_json, status: 500
end
end
...
private
def user_params
params.require(:user).permit(:first_name, :last_name, user_permissions_attributes: [ :user_id, :resource_id, :can_read, :can_update, :can_create, :can_delete ])
end
end
I am referencing this rails documentation on how to use accepts_nested_attributes_for with Strong Parameters.
However, when I 'puts user_params' from inside the users_controller this is all I see (no reference to the user_permissions):
{"first_name"=>"Joe", "last_name"=>"Shmoe"}
Here is an example of JSON I am submitting to the server (via angular $resource):
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": [
{
"organization_resource_id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
},
{
"organization_resource_id": 21,
"user_id": 10,
"can_create": true,
"can_read": true
}
],
}
Which returns this JSON:
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": [],
}
I am fairly confident this is an issue in my rails layer, but just for reference here is the angular User.js service I created to perform this RESTful interaction with the server:
angular.module('slics').service('User', [
'$resource', function($resource) {
return $resource('/api/users/:id', {
id: '#id'
}, {
update: {
method: 'PUT',
isArray: false
}
});
}
]);
Really not sure what I am missing here. It does not seem like it should be this difficult to submit nested attributes... but the more research I do the more I realize this does seem to be a pretty common Rails frustration.
Please feel free to comment if any additional context/information would be useful to include in my problem description to help troubleshoot this problem and I would be happy to provide it!
Strong params expects user_permissions_attributes, and you're submitting user_permissions.
Strong params is separate from accepts_nested_attributes_for (in fact, it has nothing to do with it), so however you define your require!/permit calls is exactly how your attributes should be submitted.
ProTip: To save you some future frustration, if you plan on updating through accepts nested attributes, you probably want to permit :id as well.
Well, you post an array of hashes, not a hash.
So this code
user_permissions_attributes: [ :user_id, :resource_id, :can_read, :can_update, :can_create, :can_delete ]
will permit such structure
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions_attributes": [
"organization_resource_id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
]
}
Try to whitelist all params at "user_permissions"
user_permissions_attributes: []
Or check out this article, to learn how to build advanced whitelists with StrongParams
http://patshaughnessy.net/2014/6/16/a-rule-of-thumb-for-strong-parameters
user_permissions_attributes: [ :user_id, :id, :can_read, :can_update, :can_create, :can_delete ]) permit :id and submitting hashes with index value..
JSON format submitting to the serve
"user": {
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": {
"0": {
"id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
},
"1": {
"id": 21,
"user_id": 10,
"can_create": true,
"can_read": true
}
}
}

Resources