THREESCALE-12434: Migrate from protected attributes to strong parameters - Part 2#4249
THREESCALE-12434: Migrate from protected attributes to strong parameters - Part 2#4249mayorova wants to merge 8 commits into
Conversation
jlledom
left a comment
There was a problem hiding this comment.
Some comments come from the other PR. Also, I still see a lot of
38e277c to
db25ecf
Compare
| @account_params ||= begin | ||
| defined_fields_names = buyer_account.defined_fields_names | ||
| allowed_attrs = defined_fields_names - %w(billing_address) + %w(name) | ||
| nested_params = { extra_fields: buyer_account.defined_extra_fields_names } |
There was a problem hiding this comment.
So, this is different from other API controller, which have only plain parameters, i.e. not nested under extra_fields.
I only did this because there was an existing test in test/integration/admin/api/accounts_controller_test.rb which was passing extra params in this way:
params: update_params.merge(extra_fields: { my_field: 4 })
I also added a check that plain parameters would also work.
Now I'm not sure if other API controllers need to accept both ways too 🤔
a676b21 to
8b5a6de
Compare
0b8ead4 to
6c69623
Compare
498f50b to
706cacb
Compare
a08b299 to
d3304a9
Compare
e2771a2 to
1abbc5c
Compare
❌ 30 blocking issues (32 total)
|
279e452 to
7981703
Compare
4b5523f to
5f946c2
Compare
5f946c2 to
22791f2
Compare
d2c96f4 to
6fe7923
Compare
24a8494 to
d30e446
Compare
7d4be7e to
8554802
Compare
8554802 to
14797a9
Compare
| authorize! :update, user | ||
|
|
||
| user.update_with_flattened_attributes(flat_params) | ||
| user.update(user_params) |
There was a problem hiding this comment.
Seems like the underlying method is not used anymore and can be removed?
porta/app/lib/fields/extra_fields.rb
73: def update_with_flattened_attributes(flattened_attrs, options = {})
There was a problem hiding this comment.
Hmm, yes, and probably assign_unflattened_attributes too.
I still see one use of unflattened_attributes=, but maybe if it's possible to get rid of it, maybe nest_extra_fields can go too...? 🤔
But I guess I was unsure if this was actually needed for something...
|
|
||
| def buyer | ||
| @buyer ||= @provider.buyers.build do |account| | ||
| # We need to get all the account params to run the spam check |
There was a problem hiding this comment.
This was indeed added explicitly in this PR: https://github.com/3scale/porta/pull/3215/changes
However, later the implementation of the spam check was changed, and this method became dead code: a798bc0
| end | ||
|
|
||
| def permitted_user_params | ||
| filter_readonly_params(user_params, User).permit(*resource.defined_fields_names, :password, :password_confirmation) |
There was a problem hiding this comment.
While resource.special_fields should be [] anyway, shouldn't we still add it to the permit list just to keep things consistent and for future proofing? Although most likely whoever ever adds such fields will notice that they don't get set and update this here.
There was a problem hiding this comment.
Actually, for User model special_fields return %i[password password_confirmation].
I decided to be provide these two fields explicitly here, to me it made more sense. special_fields is a bit too obscure, and it's not clear at all what it's supposed to mean. Having them explicitly stated in the permitted params list, on the other hand, explains the behavior of the controller better.
In fact, now looking at where special_fields is still used, I think I will try to remove this - one confusing thing less 🙃
There was a problem hiding this comment.
Your review made me look into this, and I:
- removed
special_fields, because it was not needed: 6d9e844 - while working on that, I realized that moving to strong parameters caused a regression in some APIs - passing the
annotationsparameter stopped working. I fixed it and added tests: fd9fc80
So, while annotations was inlcuded in the list of special_fields for Account model, it was not used anywhere, apparently, so it was easy to miss 🤷
The fact that there were no test also didn't help.
Hopefully, we're good now! 🤞
|
|
||
| def user_params | ||
| params.require(:user).permit(*@user.defined_fields.map(&:name), *@user.special_fields) | ||
| params.require(:user).permit(*@user.defined_fields_names, :password, :password_confirmation) |
There was a problem hiding this comment.
same question here, should we remove special_fields? How did things work before having :password, :password_confirmation permitted?
There was a problem hiding this comment.
As explained in another comment, User#special_fields returns [:password, :password_confirmation], so this change is actually fully equivalent, but more explicit.
porta/app/lib/authentication/by_password.rb
Lines 93 to 95 in ab22238
| def test_defined_fields_names | ||
| field1 = mock('field', name: 'field1') | ||
| field2 = mock('field', name: 'field2') | ||
| model = Model.new | ||
| model.stubs(:defined_fields).returns([field1, field2]) | ||
| assert_same_elements %w[field1 field2], model.defined_fields_names | ||
| end | ||
|
|
||
| def test_defined_extra_fields_names | ||
| extra_field = mock('field', name: 'extra') | ||
| builtin_field = mock('field') | ||
| model = Model.new | ||
| model.stubs(:defined_fields).returns([extra_field, builtin_field]) | ||
| model.stubs(:defined_builtin_fields).returns([builtin_field]) | ||
| assert_equal %w[extra], model.defined_extra_fields_names | ||
| end | ||
|
|
||
| def test_defined_builtin_fields_names | ||
| field1 = mock('field', name: 'field1') | ||
| field2 = mock('field', name: 'field2') | ||
| model = Model.new | ||
| model.stubs(:defined_builtin_fields).returns([field1, field2]) | ||
| assert_same_elements %w[field1 field2], model.defined_builtin_fields_names | ||
| end |
There was a problem hiding this comment.
feels like all these are redundant in the presence of test_provider_fields
There was a problem hiding this comment.
Well, I think the tests are not redundant, because each one tests a different method.
However, now I'm thinking - why didn't I use defined_fields_names_for everywhere instead of the alternatives?..
I guess the reason is that at first it hadn't occurred to me. I was relying on the existing defined_fields_names and similar, whose implementation is actually more complex, because it:
- requires a properly initialized instance - which is a bit inconvenient, and made me build the new instance of objects with proper associations before being able to derive the list of defined fields
- includes calculating the
fields_definitions_source_rootetc.
It makes me think - should I refactor the whole PR to use this simpler defined_fields_names_for(class_name)? 🤔
| let(:provider) { FactoryBot.create(:provider_account, self_domain: 'example.org') } | ||
| let(:provider) do | ||
| provider_acc = FactoryBot.create(:provider_account, self_domain: 'example.org') | ||
| FieldsDefinition.create_defaults!(provider_acc.provider_account) |
There was a problem hiding this comment.
btw, related to all places where this line was added, isn't it a little stupid that we create field definitions for master in db:seed and then the provider callback to create field definitions has unless master??
Won't it be easier to remove that line from seeds and remove that condition from the callback and have one way to do things?
The only advantage I see presently is that not all tests need these definitions while we create master for almost all tests.
But I don't ask you to modify anything. Just thinking out loud. I'm not sure that master.provider? is always true so it may lead to other small adjustments needed here and there and complicate this PR further. It's fine to keep it sweet, short and simple 👼
There was a problem hiding this comment.
Yeah, not sure why that unless master? is there 🤷
Maybe it would be better to add fields definitions creation to the factory (to avoid changes to the main application logic). But yeah, I guess the idea is to not create extra objects and associations that most of the tests don't need.
| EnforcedStyle: referrer | ||
|
|
||
| Rails/ActionOrder: | ||
| Enabled: false |
There was a problem hiding this comment.
Because it was annoying me every time 😬
Apparently, nobody ever cared about following a specific order, except @jlledom who has his own preference (which does not coincide with the default), see #4241 (comment)
So, now each controller has its own order, and as I was touching controllers, I kept getting these warnings.
We may chose to stick to the same order everywhere, and to fix all controllers and enable this rule again. But in another PR.
Or we may keep ignoring it 😬
| test "index shows users list" do | ||
| get :index | ||
| assert_response :success | ||
| end | ||
|
|
||
| test "edit shows user form" do | ||
| get :edit, params: { id: @member_user.id } | ||
| assert_response :success | ||
| end |
There was a problem hiding this comment.
I assume we have some cucumberf for this. What are we testing? nothing_raised 😅 ?
There was a problem hiding this comment.
Pretty much 😉
I think it doesn't hurt, and it's good practice to test every action of the controller.
Now, could these tests be more extensive (e.g. assert something more) - of course. But I think it's better to have a status check than not have the test at all. I'll try to add a couple of more assertions (e.g. that the drop is assigned).
| account.validate_fields! if validate_fields | ||
| account.unflattened_attributes = account_attributes | ||
| vat_rate = account_attributes[:vat_rate] | ||
| account.vat_rate = vat_rate.to_f if vat_rate |
There was a problem hiding this comment.
Here it converted vat_rate.to_f while I don't see this being done in the controllers. While I'm not impressed about seeing a money related value to use the Float data type, I just want to make sure that the change was intentional and thought through. And that what we get by the automatic ActiveRecord conversion is what we actually want.
There was a problem hiding this comment.
So, yes, this conversion in the end was not needed, because ActiveRecord converts the value automatically (e.g. from string coming from user to BigDecimal which is the actual column's format (t.decimal "vat_rate", precision: 20, scale: 2).
I've added a new test in test/unit/account_test.rb to validate the behavior - and adding .to_f doesn't change anything.
What this PR does / why we need it:
This is part 2 of the migration from protected attributes to strong parameters. See the first part in #4248
Protected attributes is an old Rails feature which was deprecated a long time ago. We were using protected_attributes_continued gem to keep it working, but now it's also discontinued and does not support Rails 7+, so it's a blocker for upgrading to Rails 7.2 for us.
This Part 2 handles the models that can have custom attributes through FieldsDefinitions - Account, User, Cinstance.
Which issue(s) this PR fixes
https://redhat.atlassian.net/browse/THREESCALE-12434
Verification steps
All tests should pass, and all features should work as before.
Special notes for your reviewer: