-
-
Notifications
You must be signed in to change notification settings - Fork 199
TransactionWrite sanitizes items #897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
TransactionWrite sanitizes items #897
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #897 +/- ##
==========================================
+ Coverage 91.56% 91.88% +0.31%
==========================================
Files 75 76 +1
Lines 3700 3856 +156
==========================================
+ Hits 3388 3543 +155
- Misses 312 313 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
expect(doc).to be_persisted | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kind of integration tests, so it makes sense to have them, but it will be great to have more thorough specs for each transactional method, like it's done for non-transactional ones:
https://github.com/Dynamoid/dynamoid/blob/master/spec/dynamoid/persistence_spec.rb#L2996-L3037
Such specs would test:
- what actual value of a nullified attribute is persisted (it either has
null
value or is removed in the item) - and how
store_attribute_with_nil_value
option affects this behaviour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking to not add tests for store_attribute_with_nil_value = true when there is an index. This fails even without transactions. I suppose I could add a test that a ValidationException is thrown in this case. And that test should exist for both transactions and non-transactions.
let(:klass_with_gsi) do
new_class(class_name: 'Ferret') do
field :name
field :age, :number
global_secondary_index hash_key: :name, range_key: :age, projected_attributes: :all, name: 'str_num'
end
end
...
context 'store_attribute_with_nil_value = true', config: { store_attribute_with_nil_value: true } do
it 'can set gsi field to nil' do
doc = klass_with_gsi.new
doc.name = 'abc'
doc.age = 1
doc.save!
doc.age = nil
doc.save! # Aws::DynamoDB::Errors::ValidationException: Invalid attribute value type
end
end
@@ -119,6 +120,7 @@ def action_request_to_update | |||
# changed attributes to persist | |||
changes = @model.attributes.slice(*@model.changed.map(&:to_sym)) | |||
changes_dumped = Dynamoid::Dumping.dump_attributes(changes, @model_class.attributes) | |||
changes_dumped = sanitize_item(changes_dumped) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems it's more difficult than just skipping nil
values. A nullified attribute of an existing item simply will not be updated at all (so old value will be kept unchanged). So correct logic is to remove such attribute at all.
It's done in the following way in non-transactional method #save
:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. The code I put in there just skips the nils which is not correct. I'll have to do the removing for all these methods like you said.
This was bothering me again today. I'll renew my efforts to fix it. |
…sted. Otherwise GSI will fail with a nil range key.
538b01f
to
527c7ef
Compare
@andrykonchin A review of this would be appreciated. Let me know if you see some areas for cleanup. |
I'll review this later this week. |
end | ||
end | ||
# rubocop:enable Lint/DuplicateBranch | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is already applied (except stringifying Hash keys) by Dynamoid::Dumping.dump_attributes
call (in save
, update_fields
, upsert
- where #sanitize_attributes
is called) so #sanitize_attributes
seems excessive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it might make sense to stringify Hash keys during dumping as well. This way it can be shared by the transactional and non-transactional methods and can be removed from the aws plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, sanitize_attributes was excessive. I removed it. The stringifying evidently is done somewhere else and I didn't need that.
end | ||
expect(doc).to be_persisted | ||
expect(doc.reload.age).to be_nil | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to have similar specs for #update_attributes
and #update_attribute
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added update_attributes, update_fields and update_fields with set().
@andrykonchin Please review again. |
TransactionWrite should call sanitize_item so that nils are not persisted. Otherwise GSI will fail with a nil range key.
Closes #885