Rails does not nest nested posted JSON Object in parameters - ruby-on-rails

I'm posting the following JSON to the rails create method in my 'user_controller' controller class.
{"user_detail_attributes":
{
"login":"dan123456",
"password":"password",
"password_confirmation":"password"
},
"surname":"REST_TEST",
"firstname":"REST_TEST",
"phone":"0456782",
"grad_year":2010,
"jobs":false,
"email":"REST_TEST#EMAIL.co.uk"
}
My user_params are as follows:
params.require(:user).permit(
:surname, :firstname, :phone, :grad_year, :jobs, :email,
user_detail_attributes: [:id, :password, :password_confirmation, :login])
Rails should then map this json to the user.
So the params variable should contain a user, inside that user should be the attributes including the user_detail_attributes.
However the user_detail attributes are appearing outside the user params variable as a completely seperate params variable, on the same level as the users params.
So, what should be happening:
user
+surname
+firstname
.
.
.
+user_detail_attributes
+login
+password
+password_confirmation
What's actually happening:
user_detail_attributes
+login
+password
+password_confirmation
user
+surname
+firstname
.
.
.
This then leads to the user being mapped to the user table in my database but no user_details being mapped to that table.
Does anyone know why this is happening and whether I can fix it? And if it is something that I need to do with the JSON format or something in the rails application.
Thanks,
Dan
EDIT: Fixed

According to the Strong Parameters Documentation
You should be doing something like this:
parameters.require(:user).permit(:surname, :firstname, :phone, :grad_year, :jobs, :email, {user_detail_attributes: [:login, :password, :password_confirmation] })
Note the {} before and after the user_detail_attributes
My test in console
hash = {user: {user_detail_attributes: { login: "dan123456", password: "password", password_confirmation: "password"}, surname:"REST_TEST", firstname:"REST_TEST", phone:"0456782", grad_year:2010, jobs:false, email:"REST_TEST#EMAIL.co.uk"} }
params = ActionController::Parameters.new(params)
parameters.require(:user).permit(:surname, :firstname, :phone, :grad_year, :jobs, :email, {user_detail_attributes: [:login, :password, :password_confirmation] })
Outuput:
{"surname"=>"REST_TEST", "firstname"=>"REST_TEST", "phone"=>"0456782", "grad_year"=>2010, "jobs"=>false, "email"=>"REST_TEST#EMAIL.co.uk", "user_detail_attributes"=>{"login"=>"dan123456", "password"=>"password", "password_confirmation"=>"password"}}

I've worked out the problem.
The JSON should be formatted as follows:
{"user":
{"user_detail_attributes":
{
"login":"dan123456",
"password":"password",
"password_confirmation":"password"
},
"surname":"REST_TEST",
"firstname":"REST_TEST",
"phone":"0456782",
"grad_year":2010,
"jobs":false,
"email":"REST_TEST#EMAIL.co.uk"
}
}
I was missing the user attribute at the start as this was how my c# JSON encoder was formatting the JSON from my user and user_detail_attribute classes.
I had a play around by creating the JSON manually and this turned out to be the fix.
Thanks for everyone's help :)

Related

Resolving ruby params.permit syntax error

params.permit(
:type,
payload: [
user: [
:id, :email, :isAnonymous, :isAdmin, :firstName, :createdAt, :updatedAt, :phone, :inviteCode, :employeeId, :activated, :lastName,
:locked, :authVersion, :isCompanyAdmin, :isEditor, :isAnalyst, :isManager, :seenWebOnboarding, :language, :hidden, :deleted, :isSuperAdmin, :hasSignedInAsLearner,
:hasCreatedCourse, :hasInvitedAdmins, :isCompanyOwner, :sendActivationReminders, :timezone, :isUnsubscribedFromEngagementEmails, :authType, :activatedDate, :shouldShowGamificationOnboarding, :depositedPoints, :name,
:identifiers
],
course: [
:id, :customerId, :title, :courseKey, :public, :icon, :createdAt, :updatedAt, :courseImageUrl, :colour, :published, :type,
:requiresEnrolment, :isSample, :completionMessage, :completionButtonUrl, :completionButtonText, :isHidden, :learnersCount
],
parentGroup: [
:id, :customerId, :name, :createdAt, :updatedAt, :parentGroupId, :welcomePageConfigId, :selfSignupConfigId, :language, :deleted,
:ssoConfigId, :cookieConsentDisabled, :usersCount, :activatedUsersCount
],
completion: [
:overallAssessmentScore, :overallLessonScore, :scoresBreakdown, :courseStartDate, :courseCompletionDate,
:courseCompletionDurationInSeconds, :completionLanguage
]
],
:timestamp).to_h
end
This is the code that I'm having an issue with. When I try making a call to this endpoint, I run into this error.
syntax error, unexpected ')', expecting =>
:timestamp).to_h
^ excluded from capture: Not configured to send/capture in environment 'development'
I've noticed if I put the timestamp before the payload in the params.permit than this issue isn't there anymore. However, this is not how the request is going to formatted and I need to follow the structure of the request as it determines a hash later on in the code. Anyone know how to resolve this?
Even though you can do this in a method call, you can't have hash-style elements in a Ruby array. You need to have a formal hash inside of the array itself.
You need to split it out explicitly:
params.permit(
:type,
{
payload: [ ... ]
}
)
From further research, it seems that having a nested attributes must be permitted last. Makes sense as to why this only occurs when timestamp is behind payload.
I'm going to deal with this by just creating a new hash with the params later on with the correct structure.
source: https://smartlogic.io/blog/permitting-nested-arrays-using-strong-params-in-rails/

Exempt an attribute in require method

I am maintaining a current Web Application (Ruby on Rails).
Our client wanted me to add this attributes contacts_attributes: [:id, :stock_holding_status] to the method below:
def meeting_log_params
params.require(:meeting_log).permit(
:id,
:stock_id,
:ir_meeting_id,
:start_at,
:end_at,
:kind,
:meeting_type,
:request_agent_company,
:request_agent_name,
:minute_taker,
:subject,
:content,
:memo,
:rating,
:country,
:city,
:attachment_1,
:remove_attachment_1,
:attachment_2,
:remove_attachment_2,
:attachment_3,
:remove_attachment_3,
interviewers_attributes: [
:id,
:meeting_log_id,
:resource_id,
:resource_type,
:_destroy
],
speakers_attributes: [
:id,
:meeting_log_id,
:resource_id,
:resource_type,
:_destroy
],
meeting_log_contacts_attributes: [
:stock_id,
:company_name,
:name,
],
ir_guests_attributes: [
:stock_id,
:department,
:title,
:last_name,
:first_name,
:department_en,
:title_en,
:last_name_en,
:first_name_en,
]
)
end
It is just that it returns me a 422 error (which means unprocessable entity) every time I used the added attribute on this method below.
Contact.update_stock_holding_statuses(meeting_log_params[:contacts_attributes])
I think it is because of the required(:meeting_log) that was before the permit method. Can you tell me how to exempt the required(:meeting_log) if I am going to use the added attribute on certain methods?
Like what I have in mind is like this:
params.require(:meeting_log ? :meeting_log : nil).permit( <all_attributes>)
I tried using ternary operators to exempt/disable the required part for me to use the added attribute which is contacts_attributes: [:id, :stock_holding_status] on a specific method and prevent the 422 from interfering. But it didn't work.
Any suggestions please.
Thank you!
How about simply
permitted_attributes = [:id,
:stock_id,
:ir_meeting_id,
:start_at,
:end_at,
:kind,
...
]
def meeting_log_params
meeting_log ? params.require(:meeting_log).permit(permitted_params) : params.permit(:permitted_params)
As far as I know, if the input parameters do not match require.permit, then you will get code 400 (not 422).
Code 422 is most likely related to the validation of the model when inside the update_stock_holding_statuses method.

Rails 6.0 validate password presence on create and update for one type of user but only update for another

I have a User model with the following attributes:
id: integer
username: string
email: string
password_hash: string
password_salt: string
account_type: string
...
My goal is to validate a user's password on create and update.
When a user with account_type: 'a' gets created or updated I want to run validates :password, :password_confirmation, presence: true.
When a user with account_type: 'b' gets updated I want to validate the password and password_confirmation presence as true, but NOT when the user gets created.
If more code or a clearer explanation would help, please let me know. Thanks in advance.
validates :password, :password_confirmation, presence: true, unless: -> { |user| user.account_type == 'b' && user.new_record? }

Rails: Whitelisting nested parameters / Permitted Parameters / Nested forms

I've been tinkering with the Rails 5.0 permit function for quite some time now, but I just can't get it right and really need some help:
I have this white listing function. And when executed I get the following response:
Unpermitted parameter: studio_users_attributes
I have this white listing function
def studio_params
params.require(:studio).permit(:name, :name_long, :address, :address2,
:zip, :city, :country, :phone,
:entity_id, { studio_users_attributes: [] } )
end
The params method returns me following:
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"VZTPSpsmNJxcqc[...]",
"studio"=>{"studio_users_attributes"=>{"0"=>{"viewing_rights"=>"Nur eigene",
"id"=>"4"}, "1"=>{"viewing_rights"=>"Alle", "id"=>"5"}}},
"commit"=>"Studio aktualisieren", "controller"=>"studios",
"action"=>"update", "id"=>"2"
}
Some help would be highly appreciated.
Philipp
In the controller you use studio_users_params.
But you send studio_users_attributes.
After some more experimenting I managed to solve it.
For the nested parameters I had to explicitly add the keys to the nested-hash:
def studio_params
params.require(:studio).permit(:name, :name_long, :address, :address2, :zip,
:city, :country, :phone, :entity_id,
studio_users_attributes: [:id, :viewing_rights] )
Leaving the hash empty (studio_users_attributes: []), would still result in
"Unpermitted parameter: studio_users_attributes "

Cannot iterate over unique elements

I have an array of errors as below:
n.errors.full_messages
# => ["Password can't be blank", "Password can't be blank", "Password is too short (minimum is 3 characters)"]
I would like to iterate over the unique elements of this array and display them on the screen.
Validation model
class Member < ActiveRecord::Base
has_one :profile
before_save { self.username = username.downcase }
has_secure_password
validates :password, presence: true, length: { minimum: 3 }
end
n.errors.full_messages.uniq do |a|
puts a
end
uniq is not working, and "Password can't be blank" appears twice in my array. Any ideas?
Password can't be blank
Password can't be blank
Password is too short (minimum is 3 characters)
# => ["Password can't be blank"]
Edit: This doesn't actually answer the question, use the answer from #BroiSatse if google brought you here.
The reason you are getting that message twice is because of has_secure_password:
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
The first bullet point appears to add validation that makes the password required.
In your model you also add
validates :password, presence: true, length: { minimum: 3 }
The presence: true part of this adds the same validation. Remove this so its:
validates :password, length: { minimum: 3 }
This should fix the problem.
uniqe with a block uses value of the block to compare the elements. What you want is:
n.errors.full_messages.uniq.each do |m|
Anyhow, I think you're approaching the problem from the wrong angle - your problem is not how to avoid displaying duplicate messages, but how this duplication happened.
You could just do a string comparison.
Have the array that is problematic to you. Go over it and save the elements one by one to a new array.
Now when the first one is added to the new array, check that the elements allready in the array don't match the new element to be added. E.g: "My string".eql? "My sting"
There might be of course a more clever way to do this in Ruby. But that would be the idea when doing it in other languages manually.

Resources