Skip to content

Commit 0b189c3

Browse files
authored
Merge pull request #917 from Dynamoid/ak/fix-sending-partition-key-and-always-dump-it-in-transactions
Fix transactional operations when primary key is of non-native DynamoDB type
2 parents 2a3a9dd + ac6d003 commit 0b189c3

15 files changed

+823
-54
lines changed

lib/dynamoid/transaction_write/delete_with_instance.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ def observable_by_user_result
3535
end
3636

3737
def action_request
38-
key = { @model_class.hash_key => @model.hash_key }
38+
key = { @model_class.hash_key => dump_attribute(@model_class.hash_key, @model.hash_key) }
3939

4040
if @model_class.range_key?
41-
key[@model_class.range_key] = @model.range_value
41+
key[@model_class.range_key] = dump_attribute(@model_class.range_key, @model.range_value)
4242
end
4343

4444
{
@@ -55,6 +55,11 @@ def validate_model!
5555
raise Dynamoid::Errors::MissingHashKey if @model.hash_key.nil?
5656
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
5757
end
58+
59+
def dump_attribute(name, value)
60+
options = @model_class.attributes[name]
61+
Dumping.dump_field(value, options)
62+
end
5863
end
5964
end
6065
end

lib/dynamoid/transaction_write/delete_with_primary_key.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ def observable_by_user_result
3434
end
3535

3636
def action_request
37-
key = { @model_class.hash_key => @hash_key }
37+
key = { @model_class.hash_key => dump_attribute(@model_class.hash_key, @hash_key) }
3838

3939
if @model_class.range_key?
40-
key[@model_class.range_key] = @range_key
40+
key[@model_class.range_key] = dump_attribute(@model_class.range_key, @range_key)
4141
end
4242

4343
{
@@ -54,6 +54,11 @@ def validate_primary_key!
5454
raise Dynamoid::Errors::MissingHashKey if @hash_key.nil?
5555
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @range_key.nil?
5656
end
57+
58+
def dump_attribute(name, value)
59+
options = @model_class.attributes[name]
60+
Dumping.dump_field(value, options)
61+
end
5762
end
5863
end
5964
end

lib/dynamoid/transaction_write/destroy.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ def observable_by_user_result
5454
end
5555

5656
def action_request
57-
key = { @model_class.hash_key => @model.hash_key }
57+
key = { @model_class.hash_key => dump_attribute(@model_class.hash_key, @model.hash_key) }
5858

5959
if @model_class.range_key?
60-
key[@model_class.range_key] = @model.range_value
60+
key[@model_class.range_key] = dump_attribute(@model_class.range_key, @model.range_value)
6161
end
6262

6363
{
@@ -74,6 +74,11 @@ def validate_model!
7474
raise Dynamoid::Errors::MissingHashKey if @model.hash_key.nil?
7575
raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
7676
end
77+
78+
def dump_attribute(name, value)
79+
options = @model_class.attributes[name]
80+
Dumping.dump_field(value, options)
81+
end
7782
end
7883
end
7984
end

lib/dynamoid/transaction_write/save.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ def action_request_to_update
121121
changes_dumped = Dynamoid::Dumping.dump_attributes(changes, @model_class.attributes)
122122

123123
# primary key to look up an item to update
124-
key = { @model_class.hash_key => @model.hash_key }
125-
key[@model_class.range_key] = @model.range_value if @model_class.range_key?
124+
key = { @model_class.hash_key => dump_attribute(@model_class.hash_key, @model.hash_key) }
125+
key[@model_class.range_key] = dump_attribute(@model_class.range_key, @model.range_value) if @model_class.range_key?
126126

127127
# Build UpdateExpression and keep names and values placeholders mapping
128128
# in ExpressionAttributeNames and ExpressionAttributeValues.
@@ -159,6 +159,11 @@ def touch_model_timestamps(skip_created_at:)
159159
@model.updated_at = timestamp unless @options[:touch] == false && !@was_new_record
160160
@model.created_at ||= timestamp unless skip_created_at
161161
end
162+
163+
def dump_attribute(name, value)
164+
options = @model_class.attributes[name]
165+
Dumping.dump_field(value, options)
166+
end
162167
end
163168
end
164169
end

lib/dynamoid/transaction_write/update_fields.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def action_request
4646
builder = UpdateRequestBuilder.new(@model_class)
4747

4848
# primary key to look up an item to update
49-
builder.hash_key = @hash_key
50-
builder.range_key = @range_key if @model_class.range_key?
49+
builder.hash_key = dump_attribute(@model_class.hash_key, @hash_key)
50+
builder.range_key = dump_attribute(@model_class.range_key, @range_key) if @model_class.range_key?
5151

5252
# changed attributes to persist
5353
changes = @attributes.dup
@@ -111,6 +111,11 @@ def add_timestamps(attributes, skip_created_at: false)
111111
result
112112
end
113113

114+
def dump_attribute(name, value)
115+
options = @model_class.attributes[name]
116+
Dumping.dump_field(value, options)
117+
end
118+
114119
class UpdateRequestBuilder
115120
attr_writer :hash_key, :range_key, :condition_expression
116121

lib/dynamoid/transaction_write/upsert.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@ def action_request
4444
changes_dumped = Dynamoid::Dumping.dump_attributes(changes, @model_class.attributes)
4545

4646
# primary key to look up an item to update
47-
key = { @model_class.hash_key => @hash_key }
48-
key[@model_class.range_key] = @range_key if @model_class.range_key?
47+
partition_key_dumped = dump(@model_class.hash_key, @hash_key)
48+
key = { @model_class.hash_key => partition_key_dumped }
49+
50+
if @model_class.range_key?
51+
sort_key_dumped = dump(@model_class.range_key, @range_key)
52+
key[@model_class.range_key] = sort_key_dumped
53+
end
4954

5055
# Build UpdateExpression and keep names and values placeholders mapping
5156
# in ExpressionAttributeNames and ExpressionAttributeValues.
@@ -91,6 +96,11 @@ def add_timestamps(attributes, skip_created_at: false)
9196
result[:updated_at] ||= timestamp
9297
result
9398
end
99+
100+
def dump(name, value)
101+
options = @model_class.attributes[name]
102+
Dumping.dump_field(value, options)
103+
end
94104
end
95105
end
96106
end

spec/dynamoid/finders_spec.rb

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
end
1515
end
1616

17-
context 'one primary key provided' do
17+
context 'a single primary key provided' do
1818
context 'simple primary key' do
1919
it 'finds' do
2020
obj = klass.create
@@ -103,21 +103,21 @@
103103
end
104104
end
105105

106-
context 'when true' do
106+
context 'when false' do
107107
it 'leads to not raising exception if model not found' do
108108
expect(klass.find('blah-blah', raise_error: false)).to eq nil
109109
end
110110
end
111111
end
112112

113-
it 'uses dumped value of partition key to update item' do
113+
it 'uses dumped value of partition key' do
114114
klass = new_class(partition_key: { name: :published_on, type: :date })
115115

116116
obj = klass.create!(published_on: '2018-10-07'.to_date)
117117
expect(klass.find(obj.published_on)).to eql(obj)
118118
end
119119

120-
it 'uses dumped value of sort key to update item' do
120+
it 'uses dumped value of sort key' do
121121
klass = new_class do
122122
range :published_on, :date
123123
end
@@ -226,19 +226,6 @@
226226
expect(klass_with_composite_key.find([[obj1.id, '1'], [obj2.id, '2']])).to match_array(objects)
227227
end
228228

229-
it 'dumps a sort key value' do
230-
klass_with_date = new_class do
231-
range :published_on, :date
232-
end
233-
234-
obj1 = klass_with_date.create(published_on: '2018/07/26'.to_date)
235-
obj2 = klass_with_date.create(published_on: '2018/07/27'.to_date)
236-
237-
expect(
238-
klass_with_date.find([[obj1.id, obj1.published_on], [obj2.id, obj2.published_on]])
239-
).to contain_exactly(obj1, obj2)
240-
end
241-
242229
it 'raises MissingRangeKey when range key is not specified' do
243230
obj1, obj2 = klass_with_composite_key.create([{ age: 1 }, { age: 2 }])
244231

@@ -274,25 +261,23 @@
274261
end
275262
end
276263

277-
context 'when true' do
264+
context 'when false' do
278265
it 'leads to not raising exception if model not found' do
279-
obj = klass.create
280-
281-
# expect(klass.find([obj.id, 'blah-blah'], raise_error: false)).to eq [obj]
282-
expect(klass.find_all([obj.id, 'blah-blah'])).to eq [obj]
266+
obj = klass.create!
267+
expect(klass.find([obj.id, 'blah-blah'], raise_error: false)).to eq [obj]
283268
end
284269
end
285270
end
286271

287-
it 'uses dumped value of partition key to update item' do
272+
it 'uses dumped value of partition key' do
288273
klass = new_class(partition_key: { name: :published_on, type: :date })
289274
objects = (1..2).map { |i| klass.create(published_on: '2018-10-07'.to_date + i) }
290275
obj1, obj2 = objects
291276

292277
expect(klass.find([obj1.published_on, obj2.published_on])).to match_array(objects)
293278
end
294279

295-
it 'uses dumped value of sort key to update item' do
280+
it 'uses dumped value of sort key' do
296281
klass = new_class do
297282
range :published_on, :date
298283
end

spec/dynamoid/persistence_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,7 +2651,7 @@ def around_update_callback
26512651

26522652
context 'primary key dumping' do
26532653
context 'new model' do
2654-
it 'uses dumped value of partition key to update item' do
2654+
it 'uses dumped value of partition key to save item' do
26552655
klass = new_class(partition_key: { name: :published_on, type: :date }) do
26562656
field :title
26572657
end
@@ -2663,7 +2663,7 @@ def around_update_callback
26632663
expect(obj_loaded.title).to eq 'Some title'
26642664
end
26652665

2666-
it 'uses dumped value of sort key to update item' do
2666+
it 'uses dumped value of sort key to save item' do
26672667
klass = new_class do
26682668
range :published_on, :date
26692669
field :title
@@ -2678,7 +2678,7 @@ def around_update_callback
26782678
end
26792679

26802680
context 'persised model' do
2681-
it 'uses dumped value of partition key to update item' do
2681+
it 'uses dumped value of partition key to save item' do
26822682
klass = new_class(partition_key: { name: :published_on, type: :date }) do
26832683
field :title
26842684
end
@@ -2691,7 +2691,7 @@ def around_update_callback
26912691
expect(obj_loaded.title).to eq 'New'
26922692
end
26932693

2694-
it 'uses dumped value of sort key to update item' do
2694+
it 'uses dumped value of sort key to save item' do
26952695
klass = new_class do
26962696
range :published_on, :date
26972697
field :title

0 commit comments

Comments
 (0)