fake_dynamo 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -1
- data/lib/fake_dynamo/api.yml +104 -0
- data/lib/fake_dynamo/db.rb +58 -0
- data/lib/fake_dynamo/storage.rb +1 -1
- data/lib/fake_dynamo/table.rb +16 -0
- data/lib/fake_dynamo/validation.rb +7 -0
- data/lib/fake_dynamo/version.rb +1 -1
- data/spec/fake_dynamo/db_spec.rb +115 -0
- metadata +8 -8
data/README.md
CHANGED
@@ -23,7 +23,9 @@ fake_dynamo --port 4567
|
|
23
23
|
````ruby
|
24
24
|
# rvmsudo fake_dynamo --port 80
|
25
25
|
AWS.config(:use_ssl => false,
|
26
|
-
:dynamo_db_endpoint => 'localhost'
|
26
|
+
:dynamo_db_endpoint => 'localhost',
|
27
|
+
:access_key_id => "xxx",
|
28
|
+
:secret_access_key => "xxx")
|
27
29
|
````
|
28
30
|
|
29
31
|
# Storage
|
data/lib/fake_dynamo/api.yml
CHANGED
@@ -557,6 +557,7 @@
|
|
557
557
|
- :string
|
558
558
|
ComparisonOperator:
|
559
559
|
- :string
|
560
|
+
- :required
|
560
561
|
- :enum: [EQ, NE, LE, LT, GE, GT, NOT_NULL, NULL, CONTAINS, NOT_CONTAINS, BEGINS_WITH, IN, BETWEEN]
|
561
562
|
ExclusiveStartKey:
|
562
563
|
- :structure:
|
@@ -661,6 +662,7 @@
|
|
661
662
|
- :string
|
662
663
|
ComparisonOperator:
|
663
664
|
- :string
|
665
|
+
- :required
|
664
666
|
- :enum: [EQ, LE, LT, GE, GT, BEGINS_WITH, BETWEEN]
|
665
667
|
ScanIndexForward:
|
666
668
|
- :boolean
|
@@ -720,6 +722,108 @@
|
|
720
722
|
- :list: member
|
721
723
|
- ConsumedCapacityUnits:
|
722
724
|
- :float
|
725
|
+
BatchWriteItem:
|
726
|
+
:input:
|
727
|
+
RequestItems:
|
728
|
+
- :map:
|
729
|
+
:key:
|
730
|
+
- :string
|
731
|
+
:value:
|
732
|
+
- :list:
|
733
|
+
- :structure:
|
734
|
+
PutRequest:
|
735
|
+
- :structure:
|
736
|
+
Item:
|
737
|
+
- :map:
|
738
|
+
:key:
|
739
|
+
- :string
|
740
|
+
:value:
|
741
|
+
- :structure:
|
742
|
+
S:
|
743
|
+
- :string
|
744
|
+
N:
|
745
|
+
- :string
|
746
|
+
SS:
|
747
|
+
- :list:
|
748
|
+
- :string
|
749
|
+
NS:
|
750
|
+
- :list:
|
751
|
+
- :string
|
752
|
+
- :required
|
753
|
+
DeleteRequest:
|
754
|
+
- :structure:
|
755
|
+
Key:
|
756
|
+
- :structure:
|
757
|
+
HashKeyElement:
|
758
|
+
- :structure:
|
759
|
+
S:
|
760
|
+
- :string
|
761
|
+
N:
|
762
|
+
- :string
|
763
|
+
SS:
|
764
|
+
- :list:
|
765
|
+
- :string
|
766
|
+
NS:
|
767
|
+
- :list:
|
768
|
+
- :string
|
769
|
+
- :required
|
770
|
+
RangeKeyElement:
|
771
|
+
- :structure:
|
772
|
+
S:
|
773
|
+
- :string
|
774
|
+
N:
|
775
|
+
- :string
|
776
|
+
SS:
|
777
|
+
- :list:
|
778
|
+
- :string
|
779
|
+
NS:
|
780
|
+
- :list:
|
781
|
+
- :string
|
782
|
+
- :required
|
783
|
+
- :required
|
784
|
+
:output:
|
785
|
+
- Responses:
|
786
|
+
- :map:
|
787
|
+
- entry
|
788
|
+
- key
|
789
|
+
- value
|
790
|
+
- entry:
|
791
|
+
- value:
|
792
|
+
- ConsumedCapacityUnits:
|
793
|
+
- :float
|
794
|
+
- UnprocessedItems:
|
795
|
+
- :map:
|
796
|
+
- entry
|
797
|
+
- key
|
798
|
+
- value
|
799
|
+
- entry:
|
800
|
+
- value:
|
801
|
+
- :list: member
|
802
|
+
- member:
|
803
|
+
- PutRequest:
|
804
|
+
- Item:
|
805
|
+
- :map:
|
806
|
+
- entry
|
807
|
+
- key
|
808
|
+
- value
|
809
|
+
- entry:
|
810
|
+
- value:
|
811
|
+
- SS:
|
812
|
+
- :list: member
|
813
|
+
- NS:
|
814
|
+
- :list: member
|
815
|
+
- DeleteRequest:
|
816
|
+
- Key:
|
817
|
+
- HashKeyElement:
|
818
|
+
- SS:
|
819
|
+
- :list: member
|
820
|
+
- NS:
|
821
|
+
- :list: member
|
822
|
+
- RangeKeyElement:
|
823
|
+
- SS:
|
824
|
+
- :list: member
|
825
|
+
- NS:
|
826
|
+
- :list: member
|
723
827
|
:client_errors:
|
724
828
|
ConditionalCheckFailed: []
|
725
829
|
|
data/lib/fake_dynamo/db.rb
CHANGED
@@ -104,10 +104,68 @@ module FakeDynamo
|
|
104
104
|
{ 'Responses' => response, 'UnprocessedKeys' => {}}
|
105
105
|
end
|
106
106
|
|
107
|
+
def batch_write_item(data)
|
108
|
+
response = {}
|
109
|
+
items = {}
|
110
|
+
request_count = 0
|
111
|
+
|
112
|
+
# validation
|
113
|
+
data['RequestItems'].each do |table_name, requests|
|
114
|
+
table = find_table(table_name)
|
115
|
+
|
116
|
+
items[table.name] ||= {}
|
117
|
+
|
118
|
+
requests.each do |request|
|
119
|
+
if request['PutRequest']
|
120
|
+
item = table.batch_put_request(request['PutRequest'])
|
121
|
+
check_item_conflict(items, table.name, item.key)
|
122
|
+
items[table.name][item.key] = item
|
123
|
+
else
|
124
|
+
key = table.batch_delete_request(request['DeleteRequest'])
|
125
|
+
check_item_conflict(items, table.name, key)
|
126
|
+
items[table.name][key] = :delete
|
127
|
+
end
|
128
|
+
|
129
|
+
request_count += 1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
check_max_request(request_count)
|
134
|
+
|
135
|
+
# real modification
|
136
|
+
items.each do |table_name, requests|
|
137
|
+
table = find_table(table_name)
|
138
|
+
requests.each do |key, value|
|
139
|
+
if value == :delete
|
140
|
+
table.batch_delete(key)
|
141
|
+
else
|
142
|
+
table.batch_put(value)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
response[table_name] = { 'ConsumedCapacityUnits' => 1 }
|
146
|
+
end
|
147
|
+
|
148
|
+
{ 'Responses' => response, 'UnprocessedItems' => {} }
|
149
|
+
end
|
150
|
+
|
107
151
|
private
|
152
|
+
|
153
|
+
def check_item_conflict(items, table_name, key)
|
154
|
+
if items[table_name][key]
|
155
|
+
raise ValidationException, 'Provided list of item keys contains duplicates'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
108
160
|
def find_table(table_name)
|
109
161
|
tables[table_name] or raise ResourceNotFoundException, "Table : #{table_name} not found"
|
110
162
|
end
|
111
163
|
|
164
|
+
def check_max_request(count)
|
165
|
+
if count > 25
|
166
|
+
raise ValidationException, 'Too many items requested for the BatchWriteItem call'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
112
170
|
end
|
113
171
|
end
|
data/lib/fake_dynamo/storage.rb
CHANGED
data/lib/fake_dynamo/table.rb
CHANGED
@@ -101,6 +101,14 @@ module FakeDynamo
|
|
101
101
|
consumed_capacity.merge(return_values(data, old_item))
|
102
102
|
end
|
103
103
|
|
104
|
+
def batch_put_request(data)
|
105
|
+
Item.from_data(data['Item'], key_schema)
|
106
|
+
end
|
107
|
+
|
108
|
+
def batch_put(item)
|
109
|
+
@items[item.key] = item
|
110
|
+
end
|
111
|
+
|
104
112
|
def get_item(data)
|
105
113
|
response = consumed_capacity
|
106
114
|
if item_hash = get_raw_item(data['Key'], data['AttributesToGet'])
|
@@ -137,6 +145,14 @@ module FakeDynamo
|
|
137
145
|
consumed_capacity.merge(return_values(data, item))
|
138
146
|
end
|
139
147
|
|
148
|
+
def batch_delete_request(data)
|
149
|
+
Key.from_data(data['Key'], key_schema)
|
150
|
+
end
|
151
|
+
|
152
|
+
def batch_delete(key)
|
153
|
+
@items.delete(key)
|
154
|
+
end
|
155
|
+
|
140
156
|
def update_item(data)
|
141
157
|
key = Key.from_data(data['Key'], key_schema)
|
142
158
|
item = @items[key]
|
@@ -20,6 +20,7 @@ module FakeDynamo
|
|
20
20
|
|
21
21
|
def validate_payload(operation, data)
|
22
22
|
validate! do
|
23
|
+
validate_request_size(data)
|
23
24
|
validate_operation(operation)
|
24
25
|
validate_input(operation, data)
|
25
26
|
end
|
@@ -151,5 +152,11 @@ module FakeDynamo
|
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
155
|
+
def validate_request_size(data)
|
156
|
+
if data.to_s.bytesize > 1 * 1024 * 1024
|
157
|
+
raise ValidationException, "Request size can't exceed 1 mb"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
154
161
|
end
|
155
162
|
end
|
data/lib/fake_dynamo/version.rb
CHANGED
data/spec/fake_dynamo/db_spec.rb
CHANGED
@@ -253,5 +253,120 @@ module FakeDynamo
|
|
253
253
|
}.to raise_error(FakeDynamo::ResourceNotFoundException)
|
254
254
|
end
|
255
255
|
end
|
256
|
+
|
257
|
+
context 'BatchWriteItem' do
|
258
|
+
subject do
|
259
|
+
db = DB.new
|
260
|
+
db.create_table(user_table)
|
261
|
+
db
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'should validate payload' do
|
265
|
+
expect {
|
266
|
+
subject.process('BatchWriteItem', {})
|
267
|
+
}.to raise_error(FakeDynamo::ValidationException)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'should fail if table not found' do
|
271
|
+
expect {
|
272
|
+
subject.process('BatchWriteItem', {
|
273
|
+
'RequestItems' => {
|
274
|
+
'xxx' => ['DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}]
|
275
|
+
}
|
276
|
+
})
|
277
|
+
}.to raise_error(FakeDynamo::ResourceNotFoundException, /table.*not.*found/i)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should fail on conflict items' do
|
281
|
+
expect {
|
282
|
+
subject.process('BatchWriteItem', {
|
283
|
+
'RequestItems' => {
|
284
|
+
'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
|
285
|
+
{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
|
286
|
+
}
|
287
|
+
})
|
288
|
+
}.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
|
289
|
+
|
290
|
+
expect {
|
291
|
+
subject.process('BatchWriteItem', {
|
292
|
+
'RequestItems' => {
|
293
|
+
'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
|
294
|
+
{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
|
295
|
+
}
|
296
|
+
})
|
297
|
+
}.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
|
298
|
+
|
299
|
+
expect {
|
300
|
+
subject.process('BatchWriteItem', {
|
301
|
+
'RequestItems' => {
|
302
|
+
'User' => [{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}},
|
303
|
+
{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
|
304
|
+
}
|
305
|
+
})
|
306
|
+
}.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'writes/deletes item in the db' do
|
310
|
+
response = subject.process('BatchWriteItem', {
|
311
|
+
'RequestItems' => {
|
312
|
+
'User' => [{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
|
313
|
+
}
|
314
|
+
})
|
315
|
+
|
316
|
+
response['Responses'].should eq('User' => { 'ConsumedCapacityUnits' => 1 })
|
317
|
+
|
318
|
+
response = subject.get_item({'TableName' => 'User',
|
319
|
+
'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
|
320
|
+
|
321
|
+
response['Item']['id'].should eq('S' => 'ananth')
|
322
|
+
|
323
|
+
subject.process('BatchWriteItem', {
|
324
|
+
'RequestItems' => {
|
325
|
+
'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
|
326
|
+
}
|
327
|
+
})
|
328
|
+
|
329
|
+
response = subject.get_item({'TableName' => 'User',
|
330
|
+
'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
|
331
|
+
|
332
|
+
response.should eq({"ConsumedCapacityUnits"=>1})
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'fails it the requested operation is more than 25' do
|
336
|
+
expect {
|
337
|
+
requests = (1..26).map { |i| { 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => "ananth#{i}" }}}} }
|
338
|
+
|
339
|
+
subject.process('BatchWriteItem', {
|
340
|
+
'RequestItems' => {
|
341
|
+
'User' => requests
|
342
|
+
}
|
343
|
+
})
|
344
|
+
|
345
|
+
}.to raise_error(FakeDynamo::ValidationException, /too many items/i)
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'should fail on request size greater than 1 mb' do
|
349
|
+
expect {
|
350
|
+
|
351
|
+
keys = { 'SS' => (1..2000).map { |i| 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + i.to_s } }
|
352
|
+
|
353
|
+
requests = (1..25).map do |i|
|
354
|
+
{'PutRequest' =>
|
355
|
+
{'Item' =>
|
356
|
+
{ 'id' => { 'S' => 'ananth' + i.to_s },
|
357
|
+
'keys' => keys
|
358
|
+
}}}
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
subject.process('BatchWriteItem', {
|
363
|
+
'RequestItems' => {
|
364
|
+
'User' => requests
|
365
|
+
}
|
366
|
+
})
|
367
|
+
|
368
|
+
}.to raise_error(FakeDynamo::ValidationException, /size.*exceed/i)
|
369
|
+
end
|
370
|
+
end
|
256
371
|
end
|
257
372
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fake_dynamo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
16
|
-
requirement: &
|
16
|
+
requirement: &70284409972980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70284409972980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70284409992000 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70284409992000
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: json
|
38
|
-
requirement: &
|
38
|
+
requirement: &70284409991540 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70284409991540
|
47
47
|
description:
|
48
48
|
email:
|
49
49
|
- ananthakumaran@gmail.com
|